c++ const学过多次了,但碰到工程代码还是不熟练。参考了其他博客,在此记录下来,以便有需要时翻看。
限定符const的作用是让变量或函数的某些地方不能改变。
const基础知识
- const在定义时候必须赋值
const int i=0; //i不能改变数值,且定义的时候必须初始化
i=1; //会报错,常量不能改变值
const int j; //报错,没初始化
//初始化方法没太大限制,可以用其他变量初始化
- const只在单个文件中有效,若想在全部文件中有效需要加extern
// 在.cc中定义并初始化了一个常量,该常量能被其他文件访问
extern const int i=0;
// .h头文件
extern const int i;
- 利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要。(但是引用和指针有限制)
int i=42;
const int ci=i;用变量初始化常量
int j=ci;用常量初始化变量
const的引用
常量的引用是指变量或者常量绑定到const对象上。
可以将常量赋给常量的引用,但不能把常量赋给变量的引用。即,左边为const int &i=右边变量和常量都可以。但不可int &=const int,因为这样可能改变值。
const int ci=1024;
const int &r1=ci;//正确,把常量赋给常量的引用
r1=42; //错误,r1是对变量的引用,不能变值
int &r2=ci;//错误,不能把常量赋给变量的引用
指针和const
- 不能把常量的地址赋给变量的指针。
const double pi=3.14;
double *pter=π//错误,不能把常量的地址赋给普通指针
const double *cptr=π//正确。此处是常量数据,非常量指针,即常量是不能找到其地址进行改变值的
*cptr=42;//错误,不能给*cptr赋值,
const指针
常量指针必须初始化,即常量的东西必须初始化
把*放const之前说明指针是常量,即不变的是指针本身的值,而非指向的那个值。
顶层cosnt,底层const
顶层const:指针本身个常量,作用于对象本身,意思是如果是指针的话那么对象是指针。例:int *const p1=&i;
底层const:指针所指的对象是一个常量。
int i = 0;
int *const p1=&i;//不能改变p1的值,是一个顶层const
const int ci=42;//不能改变ci的值,是一个顶层const
const int *p2=&ci;//允许改变p2的值,是一个底层const。int* cosnt p是顶层的
const int *const p3=p2;//靠右的const是顶层const,靠左的是底层const
const int &r=ci;//用于声明引用的const都是底层const
拷贝操作时,顶层const不受什么影响。
i=ci;//ci是一个顶层const,对此操作无影响
p2=p3;//p3顶层const的部分不影响
但底层const拷贝时,拷入和拷出的对象必须具有相同的底层const资格。
一般来说,非常量可以转换成常量,反之则不行
int *p=p3;//错误,p3包含底层const的定义,而p没有
p2=p3;//正确,p2和p3都是底层const
p2=&i;//正确,int*能转换成const int*
int &r=ci;//错误,普通的int&不能绑定到int常量上
const int &r2=i;//正确,const int &可以绑定到一个普通int上
赋值与拷贝
拷贝操作时,拷入拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行。
底层const是代表对象本身是一个常量(不可改变);
顶层const是代表指针的值是一个常量,而指针的值的内容(即对象的地址的内容)可以改变(指向的不可改变);
#include<iostream>
using namespace std;
int main(){
int a1=3; ///non-const data
const int a2=a1; ///const data 常量数据
int * a3 = &a1; ///non-const data,non-const pointer
int * a5 = *a2; //报错,不能把常量地址赋给变量的指针
const int * a4 = &a1; ///const data,non-const pointer,
const在*之前,常量数据,非常量指针。【可以记为哪个在前哪个优先级高,只适用于一const一指针的情况】
int * const a5 = &a1; ///non-const data,const pointer,只能
// *在const之前,非常量数据,常量指针。常量指针必须初始化
//常量数据,常量指针,下面两种方法等效,*左右各2个单词
int const * const a6 = &a1; ///const data,const pointer
const int * const a7 = &a1; ///const data,const pointer
return 0;
}
const修饰函数参数
代表传递过来的参数在函数内不可以改变。
void testModifyConst(const int _x) {
_x=5; ///编译出错
}
const修饰成员函数:形参后、函数体前加const
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量
#include <iostream>
using namespace std;
class Point{
public :
Point(int _x):x(_x){}
void testConstFunction(int _x) const{
///错误,在const成员函数中,不能修改任何类成员变量
x=_x;
///错误,const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量
modify_x(_x);
}
void modify_x(int _x){
x=_x;
}
int x;
};
const修饰函数返回值
(1)指针传递
如果返回const data,non-const pointer,返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。
const int * mallocA(){ ///const data,non-const pointer
int *a=new int(2);
return a;
}
int main()
{
const int *a = mallocA();
///int *b = mallocA(); ///编译错误
return 0;
}
(2)值传递
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
所以,对于值传递来说,加const没有太多意义。
所以:
不要把函数int GetInt(void) 写成const int GetInt(void)。
不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
在编程中要尽可能多的使用const,这样可以获得编译器的帮助,以便写出健壮性的代码。