第二章有点长的,今天继续加油吧。
在变量声明之前,加上const限定符可以防止他被将来不可预料的修改(被你的同事.jpg)。
const int bufSize = 512;
并且,在初始化的时候,他必须被显式地初始化。并且,除了不可修改以外,const限定的变量和原来的type表现形式是一致的。
int i = 42;
const int ci = i;
int j = ci;
const限定的整数在编译的时候,就已经hard code了。这点系统学过OS比较好理解。正因为如此,在编译不同文件时,const限定常量会被多次初始化。如果我们想在一个文件内定义,其他文件去使用他,就需要用到extern了。
// in file_1.cc
extern const int bufSize = fcn();
// in file_1.h
extern const int bufSize; // same bufSisze as defined in file_1.cc
回顾definition和declaration的区别:define多见于.c文件,实现功能;而declaration多见于.h文件,用于声明变量名称。但const限定符下的常量,我们必须在declaration时候就define,无法做到分离。
我们如何引用常量?
const int ci = 1024;
const int &r1 = ci; // ok
r1 = 42; // error: r1 是常量引用,修改无用
int &r2 = ci; // error: 不行,常量必须使用常量引用
可以bind a const reference to
int i = 42;
const int &r1 = i;
const int &r2 = 42; // reference to a literal
const int &r3 = r1 * 2; // reference to a nonconst variable
int &r4 = r * 2; // error: r4 is a plain, non const reference
Const reference may refer to a non-const object. Const reference和引用的的object无关,只限定了我们对他的行为。
Nonconst reference 指向 conference是illegal的。
A pointer 可以指向const或者是nonconst。
const double pi = 3.14; // pi is const;
double *ptr = &pil; // error: ptr is a plain pointer
const double *cptr = π // ok: cptr 需要指向一个const double,不是const不能指,这里可以和之前一样从右往左理解
*cptr = 42; // error
double dval = 3.14;
cptr = &dval; // ok: but cannot change dval through cptr
接下来看一个骚操作,理解一下从右往左的指针:
// Example 1: const pointer
int errNumb = 0;
int *const cirErr = &errNumb; // 这是一个常量指针,一直指向errNumb
// Exapmle 2: const pointer of a const
const double pi = 3.14159;
const double *const pip = π // 这是一个常量指针,一直指向常量pi
这里最后一个还是从近到远理解:
pip
我定义了一个变量叫做pipconst pip
我定义了一个常量pip*const pip
我定义了一个常量pip,他是一个指针:pip是一个常量指针double *const pip
我定义了一个常量指针pip,他指向doubleconst double *const pip
我定义了一个常量指针pip,他指向常量doubleconst double *const pip = π
我定义了一个常量,所以必须初始化;他是一个指向常量double的指针,所以我必须分配一个常量double的地址给他。当指针自身为const时,我们称之为top-level const。
当指针指向的内容为常量时,我们称之为low-level const。
When copy an object, top-level ocnsts are ignored. Low-level const is never ignored.
A const expression is an expresssion whose value cannot change and that can be evaluated at compile time.
const int max_files = 20; // yes
const int limit = max_files + 1; // yes
int staff_size = 27; // no: type not const
const int sz = get_size(); // no: initializer not const
实际上,在真实coding的时候,我们可以让编译器去辅助判断:constexpr
去判断我们变量对应的initiailizer是否为const expression。
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size(); // ok only if size is a constexpr function
constexpr
imposes a top-level const on the objects it defines.
const int *p = nullptr; // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int
其实这里,我对于const与constexpr作用没有很好的理解:感觉constexpr有点多余。我查询了其他博客,这篇博客提出一些观念:
- 是一种很强的约束,更好地保证程序的正确语义不被破坏。
- 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的- - constexpr表达式都直接替换成最终结果等。
- 相比宏来说,没有额外的开销,但更安全可靠。