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

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函数是不能改变其值的
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
constconstexpr是C语言中用于定义常量的两个关键字。 const是一个修饰符,用于声明一个不可改变的常量。在C语言中,我们可以使用const来定义一个常量,一旦被定义后,就不能再改变其值。常量一般用大写字母表示,例如: ``` const int MAX_VALUE = 100; ``` 在上述代码中,MAX_VALUE被定义为一个常量,其值为100,这意味着在程序的其他地方不能再改变MAX_VALUE的值。 constexprC++11新增加的关键字,用于在编译时求得表达式的值,并将其作为一个常量。与const类似,constexpr也用于定义常量,但它的需求更加严格。在C++中,constexpr可以用于常量的定义、函数的参数和返回值等。 constexpr的特点是在编译时求值,因此可以提高程序的运行效率。例如: ``` constexpr int square(int x) { return x * x; } int main() { constexpr int num = 5; constexpr int result = square(num); return 0; } ``` 在上述代码中,我们定义了一个constexpr函数square,它接收一个参数x,并返回x的平方。在main函数中,我们使用constexpr定义了一个常量num,并将它作为square函数的参数,得到了一个常量result。 总结来说,constconstexpr都是用于定义常量的关键字。const适用于C语言,用于声明不可改变的常量;constexpr适用于C++11及以后的版本,用于在编译时求得表达式的值,并将其作为一个常量。在实际的开发中,根据具体的需求来选择使用const还是constexpr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值