谷歌
c++ 编码规范也比较推崇尽量使用const
。
const
对指针的修饰
const
对指针的修饰就看它与星号的位置关系:
- 如果
const
在星号左边,则被指对象是常量:const int* p = &val
- 如果
const
在星号右边,则指针自身是常量:int* const p = &val
- 如果
const
在星号左右都有,则被指对象和指针都是常量:const int* const p = &val
在使用迭代器时,如果不希望改变容器元素的内容,那么就应该使用const_iterator
。
如果使用const
修饰iterator
,则得到的是T* const
指针,也就是指针自身是常量,但被指对象可以被改变。
当函数要返回一个指针或引用时,需要考虑是否允许用户可以修改此返回值,如果不允许修改,则需要加上const
限定。
const
成员函数
当成员函数不会修改成员变量时,应该为其加上const
限定:
- 一来可以让接口更为明确
- 二来可以操作
const
对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream>
class Hello { public: void Print(void) { std::cout << "non const!\n"; } void Print(void) const{ std::cout << "const!\n"; } };
int main(void) { Hello hello;
hello.Print();
const Hello chello; chello.Print();
return 0; }
|
需要理解的是:const
仅限定该成员函数不会改变成员变量,但被该成员变量指向的内存还是有可能被改变:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <cstring> #include <cstdlib>
#include <iostream>
class CTextBlock { public: CTextBlock(const char* val) { int len; if (!val) { len = 1; } else { len = std::strlen(val) + 1; }
ptext_ = new char[len]; std::memcpy(ptext_, val, std::strlen(val)); ptext_[len - 1] = '\0'; }
~CTextBlock() { if (ptext_) { delete[] ptext_; } }
char& operator[](std::size_t position) const { return ptext_[position]; }
const char* Text(void) const { return ptext_; }
private: char* ptext_ {nullptr}; };
int main(void) { const CTextBlock cctb("Hello");
std::cout << "The contents of obj are : " << cctb.Text() << "\n";
char* pc = &cctb[0]; *pc = 'J';
std::cout << "The contents of obj after modified are : " << cctb.Text() << "\n";
return 0; }
|
当一个类需要实现const
和non-const
两个版本函数时,为了避免代码重复,应该使non-const
版本调用const
版本。
为non-const
添加const
限定,使用static_cast
即可,这是安全的。
而要去除const
显示,使用const_cast
,使用前需要谨慎。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream>
class Hello { public: void print(void) const { std::cout << "const hello world!\n"; } void print(void) { std::cout << "cast to const\n"; static_cast<const Hello>(*this).print(); } };
int main(void) { const Hello chello;
chello.print();
Hello hello;
hello.print(); return 0; }
|
const 成员函数与线程安全
const
成员函数在正常情况下,不能对该对象的私有成员变量进行更改,那该函数执行的就是只读操作。
那么多线程调用该成员函数是可以的,例外的是对mutable
修饰的变量进行更改。
为了避免const
成员函数的mutalbe
变量在多线程情况下的数据竞争,需要使用互斥锁、原子锁等方式保证临界区的互斥。
当有多个原子锁且有一定顺序时,应该使用互斥锁将它们做成一个整体。