C++中的const
const告诉编译器它所约束的对象是不允许被修改的,它可以作用于任何作用域类的对象(包括类对象),函数参数和返回值,及成员函数本体,使用它可以在编译时及时发现错误。
1)const与指针
const可以修饰指针本身,也可以修饰指针所指的对象,或者限制指针与其所指对象都为const,如下:
const char* p = "hello";//non-const pointer, const data char* const p = "hello"; //const pointer, non-const data const char* const p = "hello"; //const pointer, const data
注意,const限制指针所指物不可修改时,有以下两种方式:
const char* p = "hello"; char const * p = "hello";
也就是说,const既可以位于数据类型前也可以位于数据类型后,但必须位于星号前,它们组合在一起用来修饰指针所指的对象。可以这样理解,星号前面的用来修饰被指物,星号
后面的用来修饰指针。
2)const与STL迭代器
STL迭代器与指针类似(难道不是一样的吗?),用const修饰迭代器就像声明指针为const一样,表示该迭代器不能指向不同的东西,如果要限制迭代器所指的对象不可修改,则需
要使用const_iterator,具体如下:
std::vector<int> v; ... const std::vector<int>::iterator<int> iter = v.begin(); // iter是不能更改的,类似T* const *iter++; //ok! iter++; //error, iter是const常量 std::vector<int>::const_iterator<int> iter = v.beign(); //iter不是const,所指对象是const,不能更改 *iter++; //error! iter++; //ok!
3)const修饰函数参数及返回值
对于函数声明:
const int operator*(const int& a, const int& b) { return a*b; }
它表示函数的参数和返回值都是不可以被修改的,所以下面的代码就是错误的:
int a = 3; int b = 4; int c = 5; a*b = c; //a*b返回的是一个const常量,不可以修改
4)const与成员函数
a. 被const修饰的成员函数使得操作“const对象”成为可能,例如下面的代码编译就无法通过:
1 class Head 2 { 3 public: 4 Head(string s) { pText = s; } 5 char& operator[](size_t pos) 6 { 7 return pText[pos]; 8 } 9 private: 10 string pText; 11 } 12 13 int main() 14 { 15 const Head h1("Hello"); 16 cout<<h1[1]<<endl; //成员函数operator[]不能操作const对象 17 }
此时可以添加const成员函数来操作const对象:
const char& operator[](std::size_t pos) const { return pText[pos]; }
b. 被const修饰的成员函数不改动任何非static成员变量,下面的情况除外:成员变量是一个指针类型,此时指针所指的地址不可以改变,但指针所指的对象却可以改变。比如将
上面代码中的成员变量pText改为char*类型,即声明如下:
char* pText;
同时令对应的const成员函数的返回值不再是const类型,具体修改如下:
char& operator[](std::size_t pos) const { return pText[pos]; }
那么下面const成员函数调用也是正确的:
const Head h2("Good"); char* p = &h2[0]; *p = 'J'; //此时pText所指向的字符串为“Jood”
这样的情况编译器能够通过是因为在const成员函数确实没有改变成员变量pText的值,只是改变了该值对应的变量。如果执意要在const成员函数中对成员变量进行赋值操作,那
么可以引入摆动场:mutable,它将释放在const成员函数中非static成员变量的不可改动性。
例如如下代码将通过编译:
1 class Head 2 { 3 public: 4 ... 5 size_t length() const; 6 7 private: 8 char* pText; 9 mutable size_t textLength; //表示这些变量可能经常会被改动,即使在 10 mutable bool LengthIsValid; //const成员函数里也有可能被修改 11 }; 12 13 size_t Head::length() 14 { 15 if(!LengthIsValid) 16 { 17 textLength = strlen(pText); 18 LengthIsValid = true; 19 } 20 }
5)const转型
如果两个重载函数只是在是否是常量性上有区别,要做的事几乎一样,那么可以通过将常量性转除来实现调用两次却只执行一次的功能,这样一来就避免了代码的重复,从而减
少编译时间,代码维护时间以及避免代码膨胀等问题。因为const成员函数的本意是不修改对象,而non-const成员函数却可以对对象做任意的修改,所以在const成员函数中调用
non-const是不合理也是不允许的,但是在non-const成员函数中调用const成员函数却是可行的,具体如下:
1 class Head 2 { 3 public: 4 ... 5 const char& operator[](std::size_t pos) const 6 { 7 return pText[pos]; 8 } 9 char& operator[](std::size_t pos) 10 { 11 return const_cast<char&>(static_cast<const Head&>(*this)[pos]); 12 } 13 }
上述代码中有两次转型操作,首先通过static_cast<const Head&>将类型为Head&的*this转为const Head&类型,从而调用const的成员函数,然后通过const_cast<char&>去掉const成员函数返回值的const属性。
切记:const成员函数不可以调用non-const成员函数。