1.6 Constants
const与constexpr所修饰的变量都属于恒定值。但是const变量能够在程序运行时计算。而对于constexpr变量,我们可以认为其只能由编译器计算,也就是说其位于只读的内存中,且效率较高。
constexpr int dmv = 17; // dmv是一个恒定变量 const double sqv = sqrt(var); // sqv也是一个恒定变量,其可以在运行时得到 double sum(const vector<double>&); // 函数的参数并不能被修改 vector<double> v {1.2, 3.4, 4.5}; const double s1 = sum(v); // 运行ok constexpr double s2 = sum(v); // error!!! 函数sum(v)并不是一个constant表达式
在上述例子中,如果我们想要使用函数来对constexpr变量进行赋值,那么我们必须保证该函数由constexpr修饰,且函数由编译器计算。
constexpr double square(double x) { return x*x; } constexpr double max1 = 1.4 * square(17); // 运行ok constexpr double max2 = 1.4 * square(var); // error!!! var并不是一个constant表达式 const double max3 = 1.4 * square(var); // 运行ok
函数由constexpr修饰,其也能使用non-constant参数,但这样的话该函数会变为一个非constant表达式。但是如果一个函数由constexpr修饰,那么我们需要保证该函数尽可能的简单且该函数并不能修改非本地变量。
1.7.1 The Null Pointer
如果我们代码中的指针并没有指向任何对象,或者说我们想要表示循环处于数组或者链表的末端,那么我们会将指针赋值为nullptr。无论指针是何种类型,都能够使用nullptr。我们还需要注意的是,代码中并不存在“null reference”。也就是说,引用只能针对有效的对象。
1.9.1 Assignment
引用与指针都能用来“引用”/指向一个对象,它们都表示在内存中的机器地址。但是,引用与指针的语法规则不同。
int x = 2; int y = 3; int* p = &x; int* q = &y; p = q
上述指针赋值的代码我们可以通过以下图片来理解:
int x = 2; int y = 3; int& r = x; int& r2 = y; r = r2;
上述引用赋值的代码我们可以通过以下图片来理解:
很明显,如果我们通过引用进行赋值,那么将会改变原始的引用对象。
1.10 Advice
[07] 函数长度应该尽可能短
[09] 当函数使用不同的参数类型来处理一个任务时,应该使用overloading重载。
[10] 如果一个函数需要在编译时进行计算,我们可以将其声明为constexpr。
[15] 变量的作用域范围应该尽可能小。
[19] 常用变量与局部变量的变量名应该尽可能短,而非常用变量与非局部变量的变量名应该尽可能长。
[22] 声明一个类型固定的变量时,我们更倾向使用{}进行初始化。
[26] 当我们在if判断中生命一个变量时,应该使用隐含的基于0的检测。
[27] 在进行位操作时,只使用unsigned类型的变量。
[31] 如果代码能清晰地表达你的用意,那么不要使用注释。