一、const 成员函数
首先来复习一下 const 成员函数,咱们自己实现一个字符串 MyStr,内部运用 char* 指针保存原始数据,运用 get_length() 函数获取数组的长度,因为 get 函数不会修正成员变量,能够运用 const 关键字润饰 get_length() 函数,编译期就会帮咱们查看是否修正了成员变量,如果有修正,就会在编译期报错,这样就能够维护成员变量不被修正。
class MyStr {
public:
MyStr() {...}
~MyStr() {...}
size_t get_length() const {
// _length = 1; 编译犯错
return _length;
}
private:
char* _data = nullptr;
size_t _length = 0;
}
二、logic const 和 bitwise const
编译器是怎么实现 const 成员查看的呢?在编译时它会查看该函数有没有对成员变量进行修正,比方上面代码里的赋值操作 _length = 1
就明显修正了成员变量,发现这样的操作编译器就会报警,这种校验就是 bitwise const。
但是只有 bitwise const 是不够的,想象 MyStr 中的 _data
运用了高速缓存,还有别的操作会修正字符串的长度,get_length()
函数需求改成下面这样。
size_t get_length() const {
_length = strlen(_data); // 编译器会报错
return _length;
}
更新字符串长度并没有改变 _data
的内容,从逻辑(logic)来讲符合 const 函数的定义,但编译器并不这样以为,咱们能够运用 mutable
字段让编译器不再阻拦咱们,像下面这样
MyStr {
public:
MyStr() {...}
~MyStr() {...}
size_t get_length() const {
_length = strlen(_data); // 编译器不会报错
return _length;
}
private:
char* _data = nullptr;
mutable size_t _length = 0;
}
这种逻辑上不改变数据的 const 函数就叫做 logic const。
三、编译器查看不出怎么办
上面讲了一种绕过编译器查看的状况,下面来看一种编译器查看不出来的问题,当类的成员变量为指针时,只改变指针指向的数据而不改变指针,编译器是不会以为违背 const 准则的。比方咱们供给 operater[]
操作符来访问 MyStr 的元素
class MyStr {
public:
...
char& operator[](const size_t index) const {
return _data[index];
}
private:
char* _data = nullptr;
size_t _length = 0;
}
MyStr my_str;
my_str[0] = 'a'; // 这样就经过const函数改变了成员变量指针指向的内容,而且编译器不会报错
这种状况怎么办呢?咱们一定要记得把 const 函数的返回值设置为 const
class MyStr {
public:
...
const char& operator[](const size_t index) const {
return _data[index];
}
private:
char* _data = nullptr;
size_t _length = 0;
}
MyStr my_str;
my_str[0] = 'a'; // 这样编译器就会报错,因为咱们修正了常引证
总结
最终把前面讲的总结为两点:
- 想要绕过编译器 const 查看时,对成员变量增加 mutable 润饰
- 常量函数的返回值(特别是引证和指针)一定要加 const 润饰