一、中心思想
1、将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、函数返回类型、成员函数本体;
2、编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”;
3、当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
二、简述
1、面对指针,可分为一下两种情况:
(1)出现在星号左边,=》被指内容是常量;
一下两种形式是等价的:
void f1(const Widget* pw);
void f1(Widget const* pw);
(2)出现在星号右边,=》指针自身是常量。两边都出现的话,被指内容和指针自身都是常量。
2、面对迭代器(const_iterator)
std::vector<int> vec;
(1)表示迭代器不得指向不同的东西,但它所指的内容是可以改变的
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;
++iter; //error, iter是const
(2)表示所指的的内容是不可改动的
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10 //error,*cIter是const
++cIter;
3、面对返回值
主要作用:
降低用户错误而造成的意外。例如:把“==”键入成“=”。
e.g
class Rational{......}
const Rational operator* (const Rational& lhs, const Rational& rhs)
int main()
{
Rational a, b, c;
//若无const此语句是合法的,但是内置类型则一定是不合法的。。但是依据C++潜在规定,为了避免无端地与内置类型 的“*”不兼容,在函数前面加上const
(a*b) = c;
//同时也能预防将“==”键入成“=”
if(a*b=c) ......
}
4、const成员函数
(1)两个成员函数若只是常量性不同,则可以被重载
const成员函数的重要性:
1)使得class的接口比较容易理解,知道哪个函数可以改动对象的内容,哪个不可以
2)使得“操作const对象”成为可能
e.g
#include<iostream>
using namespace std;
class TextBlock
{
public:
TextBlock(std::string a):text(a){}
const char& operator[](std::size_t position) const //针对const对象
{
return text[position];
}
char& operator[](std::size_t position) //针对非const对象
{
return text[position];
}
private:
std::string text;
};
int main()
{
TextBlock tb("Hello");
std::cout << tb[0];
tb[0] = 'X';
const TextBlock ctb("World");
std::cout << ctb[0];
ctb[0] = 'X'; //错误!!不能写一个const TextBlock
}
note:
两个成员函数都返回的都是引用,其中非const版本的是char& , 这样返回的是tb.text[0]d的引用,这样的话可以对其的值进行改变;
若返回值的类型是char,则不能对其进行重新赋值。
(2)bitwise constness和logical constness
前者认为,成员函数只有在不更改对象的任何成员变量时,也就是不更改对象内的任何一个bit,这样才可以说是const;
note:
1)许多成员函数虽然不是组具备const性质却能通过bitwise测试;
class CTextBlock
{
public :
CTextBlock():pText(new char[100]) {}
//const char& operator[](std::size_t position) const //针对const对象
//{
// return pText[position];
//}
char& operator[](std::size_t position) //针对非const对象
{
return pText[position];
}
char& operator[](std::size_t position) const
{return pText[position]; }
~CTextBlock()
{
delete[] pText;
}
private:
char* pText;//此类只包含了指针成员,不同于上述的std::string 成员
};
int main()
{
const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J'; //内容变为“Jello”
}
上述class只有指针成员pText,不同于之前的类TextBlock,用户并未改变该成员,但是却能改变所指内容的值
,但是仍然能逃过bitwise constness的测试,属于logical constness。
2)mutable:释放non-static成员变量的bitwise constness(因为编译器执意要bitwise constness,所以先释放bc)
class CTextBlock
{
public :
......
std::size_t length() const;
private:
char* pText;//此类只包含了指针成员,不同于上述的std::string 成员
mutable std::size_t textLenth;
mutable bool lengthIsValid;//mutable 使得在const的成员函数中可以改变这些成员变量
};
std:: size_t CTextBlock::length() const
{
if (!lengthIsValid) {
textLenth = std::strlen(pText);
lengthIsValid = true;
}
return textLenth;
}
(3)在const和non-const成员函数中避免代码的重复
由于重载的const和non-const版本的成员函数有许多重复的代码,所以尽量用一次编写的代码使用两次
1) 可以用const的版本实现non-const的版本,反之则不可以
class TextBlock
{
public:
......
const char& operator[](std::size_t position) const
{
......
return text[position];
}
char& operator[](std::size_t position)
{
return
const_cast<char&>(
static_cast<const TextBlock&>(*this)
[position]
);
}//运用const operator[]实现出non-const版本,反之则不行
private:
std::string text;
};
note:
此类有连个转型动作:
1、static_cast: 将普通对象加上const属性,使之可以调用const版本的成员函数;
2、const_cast: 去除返回值的const属性