Effective C++学记之03 尽可能使用const

1 const和指针的关系

对于我这个c++初学者来说,指针搭配常量让人有点小头疼。在描述一些概念比如“指向const的const指针”更让人大脑一时转不过来。

看书中的例子:

char greeting[] = "HELLO"
char* p = greeting;                        //non-const pointer,non-const data
char* const p = greeting;             //const pointer,non-const data
const char* p = greeting;             //non-const pointer,const data
const char* const p = greeting;  //const pointer,const data

记住书中总结的三句话可以理清const和指针的关系。

const 出现在*左边,表示被指物是常量    const char* p 等价于 char const *p
const 出现在*右边,表示指针自身是常量
const出现在*两边,被指物和指针都是常量

下面通过迭代器的例子来说明const和指针的关系:

std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();  //等价于声明一个T* const指针 指针自身不可改变,被指物可以改动。
*iter = 10;  //OK 改变被指物
++iter; //error iter是const

为了改变指针,可以采用const_iterator,如下:

const std::vector<int>::const_iterator cIter = vec.begin(); //等价于const T*
*cIter = 10 ;//error! 被指物是const
++cIter;   //OK 改变指针自身


2  声明为const可以帮助编译器侦测出错误用法。const 可以被加到任何作用于内的对象,函数参数,返回值,成员函数本体。
看下面的例子:

const Rational operator*(const Rational& l,const Rational& r) //重载了operator* 的操作
调用时:
Rational a,b,c
if((a*b)= c)  //为什么会有这种写法?其实本意是(a*b)== c)好不好! 使用const可以避免(a*b)被重新复制。


3 两个成员函数如果是常量性不同,可以被重载。

例:
class TextBlock{
    public:
    const char& operator[](std::size_t pos) const //①const object
    {
        return text[pos];
    }
    char& operator[](std::size_t pos)  //②non-const object
    {
        return text[pos];
    }
}
使用时:
TextBlock tb("hello"); // 匹配②non-const函数
cout << tb[0];

void print(const TextBlock& ctb) // 匹配①const函数
{
    cout << ctb[0];
}


4 编译器强制实施bitwise constness,但是在编码时应使用概念上的常量性(conceptual constness)。

bitwise constness阵营的人相信:成员函数只有在不更改对象的任何成员变量(static除外)时才可以说是const。
也就是说const成员函数不可以更改任何non-const成员变量。

不幸的是:如果一个更改了“指向物”的成员函数不能算是bitwise,但是如果其指针属于对象,那么此函数为bitwise可以被编译器编译。

看下面的例子:
class CTextBlock{
public:
    char& operator[](std:size_t position) const
    {return pText[position];} 
private:
    char* pText;  //指针属于对象的成员变量
};
该函数不适当地声明了一个const函数,而函数返回一个引用指向内部值。    由于operator[]实现代码并不更改pText,因此编译器认为是bitwise const。
但是以下操作终究还是可以改变它的值:
const cTextBlock cctb("hello");
char* pc = &cctb[0];
*pc ='J';

以上的情况编译器不会报错,但是终究还是改变了值。于是导出了logical constness,他们认为一个const成员函数可以修改它所处理的对象内的某些bits,但只在客户端侦测不出的情况下才得如此。

下面这个例子CTextBlock class实现高速缓存文本区块的长度以便应付询问:
class CTextBlock{
public:
    std::size_t lenth() const;
private:
    char* pText;
    std::size_t textLenth; //最后一次计算的文本区块长度。non-const都可能被修改
    bool lenthIsValid; //目前的长度是否有效。non-const都可能被修改
};
std::size_t CtextBlock::lenth() const;
{
    if(!lenthIsValid){
        textLenth = std::strlen(pText); //error! 在const成员函数中不能赋值给textLenth和lengthIsValid,解决方法:mutable
        lenthIsValid = true;//error!
    }
    return textLenth;
}

上面的解决方法可以使用mutable(可变的),它能放掉non-static成员变量的bitwise constness约束。
class CTextBlock{
public:
    std::size_t lenth() const;
private:
    char* pText;
    mutabe std::size_t textLenth; //最后一次计算的文本区块长度。non-const都可能被修改
    mutabe bool lenthIsValid; //目前的长度是否有效。non-const都可能被修改
};


5 当const和non-const成员函数有着等值等价的实现时,令non-const版本调用const版本可以避免代码重复。

令non-const operator[]调用其const兄弟可以避免代码重复的安全做法如下:
class TextBlock{
public:
    const char& operator[](std::size_t pos) const
    {
        。。。//各种等价check
        return text[pos];
    }
    char& operator[](std::size_t pos)
    {
        return   //调用const将op[],将返回值的const转除????
            const_cast<char&>(
                static_case<const TextBlock&>(*this)[pos]
            );
    }
    
}

说明:两次转型
(1)<const TextBlock&>(*this) 为了避免调用自己无穷递归,将*this从原始类型TextBlock& 转为const TextBlock&  。使用static_case进行强迫安全转型。
(2)从const operator[]的返回值中移除const

注:可以在non-const中调用const,但是反之从const中调用non-const存在不安全性,不提倡。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值