条款03(二):尽可能使用const

127 篇文章 7 订阅
39 篇文章 3 订阅

条款03:尽可能使用const

Use const whenever possible

const成员函数

将const实施于成员函数
目的: 确认该成员函数可以作用于const对象身上。
原因:

  • 它们使得class接口更为容易理解——便于得知哪个函数可以改动对象内容,哪个不行。
  • 它们使得“操作const对象”成为可能——为了改善C++的效率,根本的方法在于pass-by-reference-to-const的方式传递对象,而实现的前提,在于拥有const成员函数来处理取得的const对象

成员函数的重载

首先,值得注意的是,两个成员函数如果只是常量性(constness)不同,可以被重载
例子:

class TextBlock {
public:
...
    const char& operator[] (std::size_t position) const //operator[] for const对象
    { return text[position]; }

    char& operator[] (std::size_t position) //operator[] for non-const对象,进行了重载
    { return text[position]; }

private:
    std::string text;
};

TextBlock的operator[] s的使用:

TextBlock tb("hello");      
std::cout << tb[0];         //调用non-const TextBlock::operator[]

const TextBlock ctb("hello");
std::cout << ctb[0];        //调用const TextBlock::operator[]

在实际的编程中,const对象大多为pass by pointer-to-const或者passed by reference-to-const,因此对于下面这个例子更为合适:

void Print(const TextBlock& ctb)
{
    std::cout << ctb[0];   //调用const TextBlock::operator[]
    ...
}

只要重载operator[]并对不同的版本给与不用的返回类型,就可以使得const和non-const得到不同的处理:

//对于non-const:
std::cout << tb[0];     //正确!读取一个non-const
tb[0] = 'x';            //正确!写入一个non-const

//对于const:
std::cout << ctb[0];    //正确!读取一个const
ctb[0] = 'x';           //错误!写入一个const

在上面例子的错误中,是因为operator[]的返回类型出现错误——企图对一个“由const 加之的operator[]返回”的const char& 进行赋值。

另外,在上面的non-const operator[]的返回类型是一个reference to char,而不是char。如果只是返回一个char的话:

tb[0] = 'x'//错误!无法通过编译

出现这样的问题的原因,是因为如果函数的返回类型是一个内置类型,那么直接去修改返回值本身就是不合法的。即使合法,C++以by value返回对象也意味着,这样的改动其实是tb.text[0]的一个副本,而并非tb.text[0]自身。

成员函数时const意味着什么?这个概念分成两个观点:

bitwise constness

bitwise constness也称为physical constness。该观点中认为,成员函数只有在不更改对象之任何成员变量(static除外)时才可以说是const。也就是说,它不更改对象内的任何一个bit。这种观点也正是C++对于常量性(constness)的定义,因此,const成员函数不可以更改对象内任何non-static成员变量

但是,在这样的观点下,有些成员函数并不具备从const的性质,但是却可以通过bitwise的测试:
比如,一个更改了“指针所指物”的成员函数并不能算作是const,但是如果只是将指针隶属于对象,而指针所指物并不隶属于对象,那么对于这样的函数称之为bitwise const并不会引发编译器的异议。
举一个例子:

class CTextBlock {
public:
...
    char & operator[](std::size_t position) const //进行bitwise const声明,但是有些问题!
    { return pText[position] };
private:
    char* pText;
};

在上面的这个例子可以看到,此时数据不再是存储在string内,而是char*。此时,声明的operator[]为const,而该函数却返回一个reference指向对象的内部,而operator[]也确实不会更改pText。然而:

const CTextBlock cctb("hello");     //声明一个常量对象
char* pc = &cctb[0];                //调用const operator[]获得一个指针,指向cctb的数据

*pc = 'J';                          //现在,cctb的数据已经被修改了,变成了"Jello"!          

虽然声明了const,隶属于成员函数的指针也没有被修改,但是指针所指物却被修改了,最终还是更改了本身不像被修改的值。

logical constness

在这种观点下,一个const成员函数是可以修改它所处理的对象内的某些bits,但是只有在客户端侦测不出来的情况下才能进行。举一个例子,CTextBlock class需要计算文本块的长度:

class CTextBlock {
public:
...
    std::size_t length() const;
private:
    char* pText;
    std::size_t textLength;//最近一次计算的文本块长度
    bool lengthIsValid;//目前的长度是否有效
};

std::size_t CTextBlock::length() const
{
    if(!lengthIsValid) {
        textLength = std::strlen(pText);//错误!因为在const成员函数内,不能对textLength和
        lengthIsValid = true;           //lengthIsValid进行赋值!
    }
    return textLength;

在这里,length的实现显然不是bitwise constness,因为textLength 和 lengthIsValid 都可能被修改,而这两者的修改对于const CTextBlock对象而言虽然是可以接受的,但是对于C++而言,编译器坚持bitwise constness,为此:

  • 利用C++的一个与const相关的摆动场:mutable(可变的)。mutable会释放掉non-static成员变量的bitwise constness约束:
  • -
class CTextBlock {
public:
...
    std::size_t length() const;
private:
    char* pText;
    mutable std::size_t textLength;//最近一次计算的文本块长度
    mutable bool lengthIsValid;//目前的长度是否有效
};

std::size_t CTextBlock::length() const
{
    if(!lengthIsValid) {
        textLength = std::strlen(pText);//可以在const进行修改了
        lengthIsValid = true;           
    }
    return textLength;

在上面中,mutable使得这些变量即使在const成员函数内部,也可以被修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值