C++ const的详解
概述
const 是一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器哪些值是保持不变的,如果我们的代码试图去修改,编译器会给出错误提示。const可以使用在很多地方,下面是我对const进行的梳理。
1、const修饰变量
修饰变量时,表示此变量的值是只读的,不能被修改,因为不能被修改所以必须初始化。如:
const int abcd = 1234;
const int ival; //编译出错
2、const修饰指针或引用
主要看const在* 的左边还是右边。如果const在*的左边,则表示指针指向的内容为常量,如
const int *ptr; //*ptr为常量;
int const *ptr; //*ptr为常量;
如cosnt 在*的右边,则表示指针本身为常量,如
int* const ptr; //ptr为常量
两者的结合就是既不能改指针,又不能改内容:
cosnt int* const ptr; //两者都不能修改
引用和指针,两者差不多,唯一的区别是引用的定义就不能对引用进行重新绑定,所以只有不能修改引用绑定值的逻辑。
int a = 123;
const int& b = a; //不能通过b进行修改
3、const修饰函数
因为非成员函数上不允许修饰符,所以对于非成员函数,只有修饰参数和返回值。如:
int Add(int x, int y) const //编译器会报错,error C2270: “Add”: 非成员函数上不允许修饰符
{
return x + y;
}
const修饰函数参数,则函数内部不能进行改变,函数外部并不关心,与修饰变量和指针规则的一样。如
int func( const int x)
{
return ++x; //编译时报错
}
const修饰函数返回值,则返回值不能进行修改,与上面一样。
4、const修饰类对象
const修饰类对象时与const修饰变量并无实质不同,只是在于类对象的“改变”定义。
类对象的“改变”定义:改变任何成员变量的值,调用任何非const成员函数。
class myclass
{
public:
void func1();
void func2() const;
int m_iVal;
};
const myclass temp;
temp.m_iVal = 10; //编译出错:不能修改成员变量值
temp.func1(); //编译出错:不能调用非const成员函数
temp.func2(); //正确
5、const修饰类成员变量
const修饰的成员变量不能被修改,所以只能在初始化列表中被初始化,和类中的引用成员变量一样。
class myclass
{
public:
myclass(int val) : m_iVal(val) { };
const int m_iVal;
};
如果是静态 cosnt 成员变量,因为属于整个类,而不是对象,所以只能去类外部单独定义并初始化:如
class myclass
{
static const int m_iVal;
};
const int myclass::m_iVal = 10;
6、const修饰类成员函数
const修饰成员函数表示此函数不能对任何成员变量进行修改。一般const写在函数的后面,形如:int func() const;
class myclass
{
public:
int func() const { return ++m_iVal; }; //编译错误,不能进行修改
int m_iVal;
};
在成员函数调用的过程中,都有一个this指针被当做参数隐形的传递给成员函数。这个this指针,指向调用这个函数的对象,并且是const指针,不能修改其指向。
传递给const成员函数的this指针,指向一个const对象。也就是说,在const成员函数内部,这个this指针是一个指向const对象的const指针。所以就明白为啥const成员函数不能修改任何成员变量了。
因为静态成员函数没有this指针,所以静态成员函数不能声明称const的。
两个成员函数,如果只是常量性不同,可以被重载。如果两者有着实质等价的实现时,令非const版本调用const版本,避免代码的重复。
7、const修饰类成员函数与非成员函数构成重载
重载的定义:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载
初一看 成员函数 与 const 成员函数不应该构成重载,但下面的类书写时合法的:
class D
{
public:
void funcA(); //1
void funcA() const; //2
void funcB(int a); //3
void funcB(const int a); //4
};
其中 funcA 的两个函数构成了函数重载,而funcB 则编译错误。
const 发生重载的本质是:由于隐含的this形参的存在,const版本的function函数使得作为形参的this指针的类型变为指向const对象的指针,而非const版本的使得作为形参的this指针就是正常版本的指针。
调用规则:
7.1、const函数与非const函数的调用规则
const对象默认调用const成员函数,非const对象默认调用非const成员函数;
在同时存在const函数和非const重载函数的前提下,若非const对象想调用const成员函数,则需要显示的转化,例如(const Student&)obj.getAge();
若const对象想调用非const成员函数,同理进行强制类型转换const_cast < Student&>(constObj).getAge();(注意constObj一定要加括号)
7.2、当类中只有一种函数存在的情况
非const对象可以调用const成员函数或者非const成员函数
const对象只能调用const成员函数,若直接调用非const成员函数编译器会报错
而funcB 编译错误原因在于:对于非引用传参,形参是否const是等价的。
8、mutable关键字
在C++中为了突破const的限制而提供了关键字mutable。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,或类对象中。如:
class myclass
{
public:
int func() const { return ++m_iVal; }; //正确
mutable int m_iVal;
mutable int m_iVal2;
};
const myclass temp;
temp.m_iVal2++;
mutable只能修饰非静态数据成员。
9、const与define宏定义的区别
- 处理阶段不同:define是在预处理阶段。const常量在编译阶段使用。
- define仅做替换,没有类型检查。const有明确的类型,在编译阶段会进行类型检查。
- 存储方式不同:define只替换不会分配空间。const常量需要根据情况来定是否需要分配内存。有时只将它们保存在符号表中,有时进行分配。
- 集成化的调试工具中,define没法调试,const可以
10、const_cast
用于修改类型的const属性,只能改变运算对象的底层const。
语法:const_cast<type_id> (expression)
对于将常量对象转换成非常量对象的行为,我们称为“去掉const性质”,但是其内容是不变的。一旦我们去除了某个对象的const性质,编译器就不在阻止我们对该对象进行写操作了。
感谢大家,我是假装很努力的YoungYangD(小羊)。
参考资料:
https://www.jb51.net/article/118141.htm
https://www.cnblogs.com/icemoon1987/p/3320326.html
https://blog.csdn.net/xiazhiyiyun/article/details/71969618
https://blog.csdn.net/u014630623/article/details/51290954