5 常量和字符串
5.1 常量变量
在编程中,常量是在程序执行期间可能不会改变的值。
C++支持两种不同类型的常量:
- 命名常量是与标识符关联的常量值。
- 字面量是与标识符无关的常量值。
5.1.1 命名常量
在 C++ 中有三种方法可以定义命名常量:
- 常量变量。
- 带有替换文本的类似对象的宏。
- 枚举常量。
声明常量变量
初始化后其值不能改变的变量被称为常量变量。
要声明常量变量,我们将关键字const
(称为“const 限定符”)放在对象类型旁边:
const double gravity { 9.8 }; // preferred use of const before type
int const sidesInSquare { 4 }; // "east const" style, okay but not preferred
尽管 C++ 可以接受在类型之前或之后使用const
限定符,但在类型之前使用更为常见,因为它更好地遵循了标准英语语言惯例,即修饰符位于被修改的对象之前。
const变量必须初始化
Const 变量在定义时必须进行初始化,并且不能通过赋值来改变其值:
int main()
{
const double gravity; // error: const variables must be initialized
gravity = 9.9; // error: const variables can not be changed
return 0;
}
请注意,const 变量可以从其他变量(包括非常量变量)初始化:
#include <iostream>
int main()
{
std::cout << "Enter your age: ";
int age{};
std::cin >> age;
const int constAge { age }; // initialize const variable using non-const value
age = 5; // ok: age is non-const, so we can change its value
constAge = 6; // error: constAge is const, so we cannot change its value
return 0;
}
const变量的命名
在 C++ 中,常见的是使用首字母大写、前缀为 ‘k’ 的变量名,例如kEarthGravity
。
然而,由于 const 变量的行为与普通变量类似(除了不能被赋值),因此它们不需要特殊的命名约定。因此,我们倾向于使用与非常量相同的命名约定(例如earthGravity
)。
不要将const用于函数参数
在c++标准中,函数参数可以通过关键字设为常量const
:
#include <iostream>
void printInt(const int x)
{
std::cout << x << '\n';
}
int main()
{
printInt(5); // 5 will be used as the initializer for x
printInt(6); // 6 will be used as the initializer for x
return 0;
}
请注意,我们没有为 const 参数提供显式初始化器x
——函数调用中的参数值将用作初始化器x
。
然而,在现代 C++ 中,我们不会将值参数设为常量,因为我们通常不关心函数是否会更改参数的值(因为它只是一个副本,无论如何都会在函数结束时被销毁)。该const
关键字还会给函数原型带来一些不必要的混乱。
按值返回时请勿使用const
对于基本类型,返回类型的const
限定符会被忽略(您的编译器可能会生成警告)。
对于其他类型(我们稍后会介绍),通常没有必要按值返回 const 对象,因为它们是临时副本,无论如何都会被销毁。返回 const 值还会妨碍某些类型的编译器优化(包括移动语义),从而导致性能下降。
5.1.2 带有替换文本的类似对象的宏
带有替换文本的类对象宏,例如:
#include <iostream>
#define MY_NAME "Alex"
int main()
{
std::cout << "My name is: " << MY_NAME << '\n';
return 0;
}
当预处理器处理包含此代码的文件时,它会将MY_NAME
第 7 行的 替换为"Alex"
。注意,MY_NAME
是一个名称,而替换文本是一个常量值,因此带有替换文本的对象类宏也是命名常量。
优先使用常量变量而不是预处理器宏
最大的问题是宏不遵循正常的 C++ 作用域规则。一旦宏被 #defined 定义,当前文件中所有后续出现的宏名称都将被替换。如果该名称在其他地方使用,就会在不想要的地方发生宏替换。这很可能会导致奇怪的编译错误。
其次,使用宏调试代码通常更加困难。虽然源代码中会有宏的名称,但编译器和调试器永远无法看到该宏,因为它在运行之前已经被替换了。许多调试器无法检查宏的值,并且在处理宏时通常功能有限。
第三,宏替换的行为与 C++ 中的其他操作不同。因此,很容易出现无意的错误。
5.1.2 在多文件程序中使用常量变量
在 C++ 中,有多种方法可以实现在多文件程序中使用常量变量。
5.1.3 字面量
字面量是直接插入到代码中的值。例如:
return 5; // 5 is an integer literal
bool myNameIsAlex { true }; // true is a boolean literal
double d { 3.4 }; // 3.4 is a double literal
std::cout << "Hello, world!"; // "Hello, world!" is a C-style string literal