c++ const & constexpr c++98 c++11 c++14

文章目录

  • c++ const 和 constexpr 知识点总结
    • 一、const
      • 1. const修饰变量
        • 修饰普通变量(常量)
        • 修饰指针类型
        • 修饰引用类型
      • 2. const修饰函数
        • const修饰函数参数
        • const修饰函数返回值
        • const修饰成员函数(this指针)
      • 3. const修饰对象
      • 4. 常量数组,常量指针数组,常量引用数组
    • 二、constexpr
      • 1. constexpr特性和const之间的差异
      • 2. constexpr 在c++11中的规范
      • 3. constexpr 在c++14 17 中的规范

c++ const 和 constexpr 知识点总结


一、const

1. const修饰变量

修饰普通变量(常量)

const int a = 0; // 必须赋初始值,然后不能被重新赋值

  • 默认情况下,const对象被设定为仅在文件内有效。如果需要多文件共享const对象,需要在const对象的声明和定义处加上extern修饰。
file1.cpp
	extern const int aext = 100;
file2.cpp
	extern const int aext; // 使用的是file1.cpp中的aext变量
修饰指针类型

分为顶层const和底层const

  • 底层const(指向常量的指针)
    const int *p; // 修饰的是指针指向的对象,表示指向的是常量对象。可以使用非常量变量取地址初始化,只是限制不能通过p来改变这个变量的值
  • 顶层const:(常量指针)
    int * const p = nullptr; // 修饰的是指针本身,表示常量指针。 常量对象必须被初始化
  • 顶层+底层:(指向常量的常量指针)
    const int * const p = nullptr; // 必须被初始化,可以使用非常量对象取地址初始化
  • 顶层和底层的一般概念:
    • 顶层可以理解为对象本身是常量; 底层则与指针和引用等符合类型的基本类型部分有关,引用都是底层const。
    • 顶层const对于拷贝操作来说可以忽略const属性,这与后面的函数形参类型原理一致。
    const int a = 32; // a为顶层
    const int &r = a; // r为底层,用于声明引用的const都是底层
    const int * const p = &a;  // 指针既可以为顶层,也可以为底层
    int b = a; // 正确,顶层属性忽略
    
修饰引用类型
  • 常引用变量能接收非常量左值、常量左值、右值。
    • 常量引用可以接收表达式,只要表达式的值能转成常量类型。(但是不能接收返回局部变量和数值常量如0的函数表达式)
int a = 0; 
const int b = 0;
int &b = a; 
const int &aa = a; // 非常量左值,相当于const int *p能指向一个非常量变量。
const int &bb = b; // 常量左值(普通引用不能引用常量值)
const int &cc = 0; // 右值(普通引用不行)
const int &dd = a + b; // 接收表达式(普通引用不行),可以理解为常引用的是一个a+b返回的临时变量。

2. const修饰函数

const修饰函数参数
  • 当作为函数形参的时候,顶层const属性可以被忽略。
    • 非const可以传递给顶层const形参
    • 顶层const形参和非const形参会造成重定义
int a = 23;  
void print(const int aa)  { };
print(a); // 非const变量可以传递给const属性形参
void print(int aa) { };  // 错误,和const版本重定义
void print( int * const p);
void print(int *p);  // 错误,与顶层const版本重定义
void print(const int *p); // 单独的底层const版本, 指向的是常量对象,不能通过指针改指向变量的值。可以接收非常量参数
int a = 9;
  • 底层const属性不会被忽略
void print(int *p);
void print(const int *p); // 底层const版本,不会重定义
void print(const int a);
void print(const int &a);  // 引用是底层的,不会重定义
const修饰函数返回值
  • 函数返回普通const变量(值传递),值返回对于const与非const修饰意义不大
  • 函数返回const 指针
  • 函数返回const引用
const int print(); // 值传递变量到外部,相当于拷贝一份内容到外界接收地址
const int a = print(); 
int a = print(); // 非常量也可以接收返回值,所以返回const意义不大
const int *printConst(); // 返回的常量对象
const int *a = printConst(); // 只能由const指针接收
const int &r = printR(); // 由const int &接收返回值。前提不能返回局部变量和数值常量如0。
const修饰成员函数(this指针)
  • const修饰成员函数时,const关键字放在函数后面。此时类成员函数时实质上修饰的是成员函数的this指针。
  • const修饰成员函数时,不能改变对象的成员并且不能调用非const成员函数,因为非const成员函数可以修改数据成员。
  • 非const成员函数可以调用const成员函数,const成员函数可以访问非const变量但不能改变他们的值
  • static函数不能被const修饰
class A {
public:
	void print() const; // const成员不能访问非const成员函数,能只读访问非const变量
	void print1(); // 非const成员能访问const成员函数和成员变量。
	int a;
	const int a;
} 

3. const修饰对象

  • const对象或者const对象指针只能访问const成员函数,只读访问成员变量。
Class ClassA {
public:
	void printC() const;
	void print();
	const int ac = 9;
	int a;
}
const ClassA cls;
cls.printC(); 
cls.print(); // 错误 不能访问非const函数
::std::cout << cls.ac << cls.a << ::std::endl; // 可以只读访问非const的变量
cls.a++; // 不能改变非const成员变量

4. 常量数组,常量指针数组,常量引用数组

  • 常量数组可以使用大括号 { }初始化。
    const int arr[3] = { 1, 2, 3};
  • 常量指针数组:同样必须初始化,可以使用大括号{ }来初始化
int ia = 3;
int ib = 4; 
int ic = 3; 
const int *arr[3] = { &ia, &ib, &ic };
  • 常量引用数组: 没有这种语法,引用本身是个别名,不占内存(实质上和占指针大小内存),编译器不知道给引用数组分配多少内存。

    关于引用内存的讨论


二、constexpr

1. constexpr特性和const之间的差异

  • 常量表达式:指的是在编译阶段就能得到计算结果的表达式
  • constexpr 变量,在编译阶段就能确定结果值的变量
    c++11新规定,constexpr声明的变量表示使用编译器来验证变量的值。如果编译阶段不能确定常量结果值,编译会报错。
    constexpr修饰的指针只能初始化为nullptr或者0或者某个固定地址中的对象,因为必须在编译阶段就确定常量属性,函数内的变量通常地址不固定。(有固定地址:全局变量,static变量)
  • const修饰的变量可以在编译阶段确定值,也可以在运行阶段确定值。constexpr相对于const实质上是把常量值的确定时间提前到编译阶段。
constexpr int a = 33; // 常量表达式,全局变量
int b = 33; // 全局变量

const int *p; // 表示p是指向常量的指针
constexpr int *p = nullptr; // 表示常量指针,转移成了顶层指针,且在编译阶段要确定实际值。
constexpr const int *pp = &a; // constexpr修饰pp是常量指针,const表示指向整型常量,a必须在编译时有地址(static,全局)。
constexpr int *p = &b; 
  • constexpr函数
    constexpr函数如果返回常量表达式,则在编译阶段,调用这个函数的地方会被直接替换成结果值;如果是运行时才确定,则返回的不是常量表达式;另外常量表达式在代码中和内联函数一致,在调用点展开,因此定义位置和内联函数一样,放在头文件中。
constexpr int getCount(int count) { return count * 3;};
int ia = 3;
::std::array<int, getCount(3)> arr; // 返回常量表达式
::std::array<int, getCount(ia)> arr; // 编译错误,返回的不是常量表达式

2. constexpr 在c++11中的规范

  • c++11 规定constexpr函数不得包含多余一个可执行语句,也就是说constexpr函数的函数体只能有一条语句,可以用条件运算符?: 或者递归来编写逻辑。
    cosntexpr int print(int a) { return a > 4 ? a : 4; };
  • constexpr函数仅限于传入和返回字面类型。也就是在编译阶段能确定值的值。所有内建类型除了void都符合。
    constexpr int print(double x = 3.3, int a = 3);

3. constexpr 在c++14 17 中的规范

  • c++14 规定constexpr函数内可以有多条语句
constexpr int printTt(int a)
{
	if (a > 4) {
		return a;
	} else {
		return 4;
	}
}
  • constexpr传入和返回值可以包含void
    constexpr void print(void);

constexpr在c++98,c++11,c++14的实现


备注:

  • const static 变量的声明式和定义式; 当要对const static变量取地址时,需要给出定义式。
Class ClassA {
public:
	const static int a = 3; // 声明式
}
xxx.cpp
const int ClassA::a; // 定义式,声明式已经赋了初始值,定义式不用在赋值。
  • 类中const成员函数如果想要改变类中的non-static成员变量,可以用mutable修饰变量
Class classA {
public:
	void changeInnerValue() const;
	mutable int aa; 
}
void ClassA::changeInnerValue() const 
{
	aa = 22; // 如果aa不用mutable修饰,const函数是不能改变其值的
}

你可能感兴趣的:(c++,c++)