0%

const和constexptr

const 和 constexpr

const

参考链接

const 修饰变量

无论const修饰全局还是函数的局部变量,那么该变量都不能被程序员显式的修改,否则编译器会报错。但是变量本质都是地址,因此可以对他取地址给指针,然后通过指针再去修改该值。这样操作编译器不会报错。例如:

1
2
3
const int N = 1000;
int* p = const_cast<int*>(&N);
*p = 102; // 这样就将N的值修改了
  • 全局 const 变量:存储在只读存储区,使用上面的方法可以修改 会导致运行错误
  • 函数局部的 const 变量:存在栈里,使用上面的方法修改它的值,且不会产生运行错误

c++ 对 const 会对默认类型的变量(非自定义的结构体成员变量,常规的int float 等会优化)在编译时期会优化,上面的例子中 会将所有地方的 N 像宏定义一样直接替换为 1000;例如

1
2
cout << N << " " << *p;
// N: 输出1000 *P输出102

因此 推荐将变量定义为 const 类型 这样编译器会直接替换为值,后面就减少一次内存访问请求 相对宏定义的优势就是 有类型检查

const 类对象

定义 const 类对象 或者 结构体变量一样,代表他们的成员都不能被修改。并且,const 类对象只能调用它的 const 成员函数,而不能调用普通的非const成员函数。

img

const指针

  • 一种指 指向的地址的内容不可以修改 但是它可以转而指向别的地址 (结合最开始的例子可以看出 编译器是通过 “指针的类型” 来判断是否只读的,因此如果普通指针指向const的变量地址,它的值仍然可以修改) const int *p
  • 指向的地址可以修改,但是它不能再指向别人 int * const p = &a ( 就看const 后面跟的是啥 啥就不能变)

const 成员函数

代表该成员函数 不能修改类中的成员变量,同时也正是这个特性,const 类的对象只能调用 ocnst 修饰的成员函数

constexpr

参考链接

constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

编译时常量/常量表达式

  • 必须是可以在编译阶段被识别的。比如模版的参数/数组的大小
  • 例如 const int a = 5; 此时 5 就是一个常量表达式 sizeof() 就是个常量表达式 在编译时期就能确定 可以能用来初始化数组大小 case语句的分支等
1
2
3
4
5
6
template <int N>
class fixed_size_list
{ /*...*/ };

fixed_size_list<X> mylist; // <-- X必须是字面值类型
int numbers[X]; // <-- X必须是字面值类型

特性

constexpr 在修饰函数的时候

  • 如果函数的入口参数 是使用的字符常量,在编译时期可以确定 那么这个函数就是常量表达式 返回值可以直接用来初始化数组大小
  • 如果入口参数的值编译时期无法直接确定 那么他就是个普通函数

constexpr的好处:

  1. 是一种很强的约束,更好地保证程序的正确语义不被破坏。
  2. 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
  3. 相比宏来说,没有额外的开销,但更安全可靠