C++中常量成员函数的含义

C++中常量成员函数的含义

本文内容来源:《C++必知必会》

使用常量成员函数可以改变对象的逻辑状态,虽然对象的物理状态没有发生改变。考虑如下代码,它定义了一个类X:

class X{
public:
    X():buffer_(0),isComputed_(false){}
    //...
    void setBuffer(){
        int *tmp = new int[MAX];
        delete [] buffer_;
        buffer_ = tmp;
    }//setBuffer
    
    void modifyBuffer(int index, int value) const{ 
        buffer_[index] = value; //Valid but not suggested! 
    }//end of modifyBuffer
    
    int getValue() const{
        if( !isComputed_){
            computedValue = expensiveOperation(); //Error!
            isComputed_ =true; // Error!
        }
        return computedValue_;
    }//end of getValue
private:
    static int expensiveOperation();
    int *buffer_;
    bool isComputed_;
    int computeValue_;
}

  

setBuffer函数必须是非常量的,因为它要修改其所属的X对象的一个数据成员。然而,modifyBuffer可以被合法地被标为常量,因为它没有修改X对象,它只是修改X的buffer_成员所指向的一些数据。这种做法是合法的,但是很不道德。

有时一个被声明为常量的成员函数必须要修改其对象,这常见于利用"lazy evaluation"机制来计算一个值时,换句话说,只有当第一次提出请求,才计算值,目的在于在该请求根本没有发出的其余情形下,让程序运行得更快。在这种情况下会有一个进行转型犯错的诱惑,为的是能够让事情变得更好,即将该成员函数声明为常量。

int getValue() const{
    int (!isComputed_){
        X *const aThis = const_cast<X *const>(this);
        aThis->computedValue = expensiveOperation();
        aThis->isComputed_ = true;
    }
    return computedValue_;
}
千万抵制住这个诱惑!处理这种情形的正确方式是将有关数据成员声明为mutable:
class X{
public:
    //...
    int getValue() const{
        int (!isComputed_){
            computedValue = expensiveOperation();
            isComputed_ = true;
        }
        return computedValue_;
    }
private:
    mutable bool isComputed_;  //现在可以修改了。
    mutable int computedValue_; //现在可以修改了。
}

类的非静态数据成员可以声明为mutable,这将允许它们的值可以被该类的常量成员函数(当然也包括非常量成员函数)修改,从而允许一个“逻辑上为常量”的成员函数被声明为常量,虽然其实现需要修改该对象。

以下例子可以解释函数重载解析是如何区分一个成员函数的常量和非常量版本的。

class X{
public:
    //...
    int &operator[] (int index);
    cost int &operator[](int index) const;
};

int i = 12;
X a; 
a[7] = i;// this 是 X *const,因为a是非常量
const X b;
i = b[i]; // this 是 const X *const, 因为b 是常量

考虑如下具有两个常量参数的非成员二元操作符

X operator+(const X &, const X &);

如果决定声明一个与此重载操作符对应的成员形式的对应物,应该将其声明为常量成员函数,此目的是为了保持左实参的常量性质。

class X{
public:
    //...
    X operator+(const X &rightArg); // 左边的参数是非常量
    X operator+(const X &rightArg) const; //左边的参数是常量
};
 
总结:
常量成员函数声明:如:int get() const;
规则:
1.常量成员函数不修改对象。
2.常量成员函数在定义和声明中都应加const限定
3.非常量成员函数不能被常量成员函数调用,但构造函数和析构函数除外。
4.常量(const对象)对象只能调用常量成员函数。(const对象的数据成员在对象寿命周期内不能改变,因此其只能调用常量成员函数)。
意义:
1.使成员函数的意义更加清楚,将成员函数分修改对象和不修改对象两类。
2.增加程序的健壮性,常量成员函数企图修改数据成员或调用非常量成员函数,编译器会指出错误。
原因:
     对于X类型的非常量成员函数而言,其this指针的类型是 X * const,该指针自身是常量;但是对于X类型的常量成员函数而言,其this指针的类型是const X * const,是一个常量指针。
 

 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

posted @ 2017-09-04 14:51  actually96  阅读(773)  评论(0编辑  收藏  举报