默认情况下,浮点文字的类型为 double 。要使它们成为 float 文字,应使用 f (或 F )后缀:

1
2
3
4
5
6
7
8
9
#include <iostream>

int main()
{
std::cout << 5.0 << '\n'; // 5.0 (no suffix) is type double (by default)
std::cout << 5.0f << '\n'; // 5.0f is type float

return 0;
}
1
float f { 4.1 }; // warning: 4.1 is a double literal, not a float literal

由于 4.1 没有后缀,因此文字的类型为 double ,而不是 float 。当编译器确定文字的类型时,它并不关心您对文字执行的操作(例如,在本例中,使用它来初始化 float 变量)。由于文字的类型 ( double ) 与用于初始化的变量的类型 ( float ) 不匹配,因此必须将文字值转换为 float 因此它可以用于初始化变量 f 。将值从 double 转换为 float 可能会导致精度损失,因此编译器将发出警告。

1
2
float f { 4.1f }; // use 'f' suffix so the literal is a float and matches variable type of float
double d { 4.1 }; // change variable to type double so it matches the literal type double

要使用八进制文字,请在文字前面加上 0(零):

1
2
3
4
5
6
7
8
9
#include <iostream>

int main()
{
int x{ 012 }; // 0 before the number means this is octal
std::cout << x << '\n';
return 0;
}
//10

在 C++14 及以上版本中,我们可以通过使用 0b 前缀来使用二进制文字:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main()
{
int bin{}; // assume 16-bit ints
bin = 0b1; // assign binary 0000 0000 0000 0001 to the variable
bin = 0b11; // assign binary 0000 0000 0000 0011 to the variable
bin = 0b1010; // assign binary 0000 0000 0000 1010 to the variable
bin = 0b11110000; // assign binary 0000 0000 1111 0000 to the variable

return 0;
}

由于长文字可能难以阅读,C++14 还添加了使用引号 (‘) 作为数字分隔符的功能。

数字分隔符纯粹是视觉上的,不会以任何方式影响字面值。

默认情况下,C++ 以十进制输出值。但是,您可以通过使用 std::decstd::octstd::hex I/O 操纵器来更改输出格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main()
{
int x { 12 };
std::cout << x << '\n'; // decimal (by default)
std::cout << std::hex << x << '\n'; // hexadecimal
std::cout << x << '\n'; // now hexadecimal
std::cout << std::oct << x << '\n'; // octal
std::cout << std::dec << x << '\n'; // return to decimal
std::cout << x << '\n'; // decimal

return 0;
}
1
2
3
4
5
6
12
c
c
14
12
12

要使用 std::bitset ,我们可以定义一个 std::bitset 变量并告诉 std::bitset 我们要存储多少位。位数必须是编译时常量。 std::bitset 可以使用整数值(任何格式,包括十进制、八进制、十六进制或二进制)进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bitset> // for std::bitset
#include <iostream>

int main()
{
// std::bitset<8> means we want to store 8 bits
std::bitset<8> bin1{ 0b1100'0101 }; // binary literal for binary 1100 0101
std::bitset<8> bin2{ 0xC5 }; // hexadecimal literal for binary 1100 0101

std::cout << bin1 << '\n' << bin2 << '\n';
std::cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it

return 0;
}
1
2
3
11000101
11000101
1010

在 C++20 和 C++23 中,我们通过新的格式库 (C++20) 和打印库 (C++23) 提供了更好的选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <format> // C++20
#include <iostream>
#include <print> // C++23

int main()
{
std::cout << std::format("{:b}\n", 0b1010); // C++20
std::cout << std::format("{:#b}\n", 0b1010); // C++20

std::println("{:b} {:#b}", 0b1010, 0b1010); // C++23

return 0;
}

constexpr(“常量表达式”的缩写)变量必须使用常量表达式进行初始化,否则将导致编译错误。

1
2
3
4
5
6
#include <iostream>
int main()
{
constexpr int sum {4+5}; //ok
constexpr int myAge{age}; //error
}

const 函数参数被视为运行时常量(即使提供的参数是编译时常量)。

函数参数不能声明为 constexpr ,因为它们的初始化值直到运行时才确定

在现代 C++ 中,术语 inline 已演变为“允许多个定义”的意思。因此,内联函数是一种允许在多个翻译单元中定义的函数(不违反 ODR)。

main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

double circumference(double radius); // forward declaration

inline double pi() { return 3.14159; }

int main()
{
std::cout << pi() << '\n';
std::cout << circumference(2.0) << '\n';

return 0;
}

math.cpp:

1
2
3
4
5
6
inline double pi() { return 3.14159; }

double circumference(double radius)
{
return 2.0 * pi() * radius;
}

请注意,这两个文件都有函数 pi() 的定义——但是,因为该函数已被标记为 inline ,所以这是可以接受的,并且链接器将删除它们的重复项。如果您从 pi() 的两个定义中删除 inline 关键字,您将遇到 ODR 违规(因为不允许非内联函数的重复定义)。

避免使用 inline 关键字,除非您有特定的、令人信服的理由这样做(例如,您在头文件中定义这些函数或变量)。

5.8 — Constexpr 和 consteval 函数

有点迷迷糊糊

std::string 不要和std::cin 一起使用。

因为:std::cin 在遇到“ ”的时候就会截断。

std::string剩下的内容会被存在缓存里面,然后等待着下次的std::cin

代替的是:使用**std::getline()**。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string> // For std::string and std::getline

int main()
{
std::cout << "Enter your full name: ";
std::string name{};
std::getline(std::cin >> std::ws, name); // read a full line of text into name

std::cout << "Enter your favorite color: ";
std::string color{};
std::getline(std::cin >> std::ws, color); // read a full line of text into color

std::cout << "Your name is " << name << " and your favorite color is " << color << '\n';

return 0;
}
1
2
3
Enter your full name: John Doe
Enter your favorite color: blue
Your name is John Doe and your favorite color is blue

std::ws

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>

int main()
{
std::cout << "Pick 1 or 2: "; //输入2和\n
int choice{};
std::cin >> choice; //抓住了2但是留下了\n

std::cout << "Now enter your name: ";
std::string name{};
std::getline(std::cin >> std::ws, name); // note: added std::ws here

std::cout << "Hello, " << name << ", you picked " << choice << '\n';

return 0;
}

std::getline(std::cin, str) 将从标准输入流 (std::cin) 中读取一行文本,并将其存储在字符串 str 中。这个函数通常用于从用户输入中读取一行文本。

std::ws:意思就是说忽略:\n

另请注意, std::string::length() 返回无符号整数值(最有可能是 size_t 类型)。如果要将长度分配给 int 变量,则应该 static_cast 它以避免编译器出现有关有符号/无符号转换的警告:

1
int length { static_cast<int>(name.length()) };

static_cast是显性转换

在 C++20 中,您还可以使用 std::ssize() 函数来获取 std::string 的长度作为有符号整数值:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <string>

int main()
{
std::string name{ "Alex" };
std::cout << name << " has " << std::ssize(name) << " characters\n";

return 0;
}

我们可以通过在双引号字符串文字后面使用 s 后缀来创建类型为 std::string 的字符串文字。 s 必须小写。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string> // for std::string

int main()
{
using namespace std::string_literals; // easy access to the s suffix

std::cout << "foo\n"; // no suffix is a C-style string literal
std::cout << "goo\n"s; // s suffix is a std::string literal

return 0;
}

std::string_view

1
#include <string_view>

C++17

当字符串只读的时候,就用:std::string_view。这样可以避免不必要的复制

std::string_view 的优点之一是它的灵活性。可以使用 C 样式字符串、 std::string 或另一个 std::string_view 来初始化 std::string_view 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <string_view>

int main()
{
std::string_view s1 { "Hello, world!" }; // initialize with C-style string literal
std::cout << s1 << '\n';

std::string s{ "Hello, world!" };
std::string_view s2 { s }; // initialize with std::string
std::cout << s2 << '\n';

std::string_view s3 { s2 }; // initialize with std::string_view
std::cout << s3 << '\n';

return 0;
}

std::string_view 参数将接受许多不同类型的字符串参数

std::string_view**不会隐式转换为**std::string

默认情况下,双引号字符串文字是 C 样式字符串文字。我们可以通过在双引号字符串文字后面使用 sv 后缀来创建类型为 std::string_view 的字符串文字。 sv 必须小写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string> // for std::string
#include <string_view> // for std::string_view

int main()
{
using namespace std::string_literals; // access the s suffix
using namespace std::string_view_literals; // access the sv suffix

std::cout << "foo\n"; // no suffix is a C-style string literal
std::cout << "goo\n"s; // s suffix is a std::string literal
std::cout << "moo\n"sv; // sv suffix is a std::string_view literal

return 0;
}