C/C++编程:常量成员函数与mutable

1060 篇文章 307 订阅

我们可以定义具名的常量对象或者变量对象。换句话说,一个名字指向既可以是一个保存不可变值的对象,也可以是一个保存可变值的对象。

为了使得不可变性不局限于内置类型的简单常量的定义,我们必须能定义可操作用户自定义const对象的函数。对独立函数而言,这意味着函数可接受const T&参数。对类而言,这意味着我们斌能定义操作const对象的成员函数。

常量成员函数

class Date{
    int y, m, d;
public:
    int day() const {return d;}
    int month() const {return m;}
    int year() const;
    void add_year(int n) {  y += n ;};
};

函数声明中(空)参数列表后的const指出这些函数不会修改Date的状态。

自然的,编译期会捕获试图违法此承诺的代表,比如:

int Date::year() const {
    return ++y;   // 错误,试图在const函数中改变成员值
}

当const成员函数在类外时,必须使用const后缀:

int Date::year() {  // 错误,在成员函数类型中漏掉了const
    return y;   
}

const和非const对象都可以调用const成员函数,而非const成员函数只能被非const对象调用:

void f(Date &d, const Date &cd){
     d.year();
     d.add_year(1);
     
     cd.year();
     cd.add_year(1);  // error, 不能改变const Date的值
}

物理常量性和逻辑常量性

有时,一个成员函数逻辑上是const,但是它仍然需要改变成员的值。即对一个用户而言,函数看起来不会改变起对象的状态,但它更新了用户不能直接观察的某些细节。这通常叫做逻辑常量性。比如,类Date可能有一个返回字符串表示的函数,构造这个字符串表示非常耗时,因此,保存一个拷贝,在反复要求获取字符串表示时可以简单的返回此包括(除非Date的值已经被改变),这就很有意义了。更复杂的数据结构常使用这种缓存值的技术,现在我们来讨论如何对Date使用这个技术:

class Date{
public:
	string string_rep() const;
private:
	bool cache_valid;
	string cache;
	void compute_cache_value();  //填入缓存
};

从用户的角度来看,string_rep并没有改变起Date的状态,因此它显然应该是一个const成员函数。但是另一方面,有时必须改变成员cache和cache_valid,这种设计才能生效。

此问题也可以通过使用类型转换来解决,比如const_cast。但是,也存在非常优雅的,不破坏类型规则的方法。

mutable

我们可以将一个类成员定义为mutable,表示即使是在const对象中,也可以修改此成员:

class Date{
public:
	string string_rep() const;  //字符串表示
private:
	mutable bool cache_valid;
	mutable string cache;
	void compute_cache_value() const;  //填入(可变的)缓存
};

string::Date::string_rep() const{
	if(!cache_valid){
		compute_cache_value();
		cache_valid = true;
	}
	return cache;
};

void f(Daye d, const Date cd){
	d.string_rep();   //ok
	cd.string_rep();  //ok
}

间接访问实现可变性

对于小对象的表示形式只有一小部分允许改变的形式,将成员声明为mutable是最合适的。但在更复杂的情况下,通常更好的方式是将需要改变的数据放在一个独立对象中,间接的访问它们:

struct cache{
	bool cache_valid;
	string rep;
};

class Date{
public:
	string string_rep() const;  //字符串表示
private:
	cache * c;  //在构造函数中初始化
	void compute_cache_value() const;  //填入cache指向的内存
};

string::Date::string_rep() const{
	if(!c->valid){
		compute_cache_value();
		c->valid = true;
	}
	return c->rep;
}

这种支持缓存的编程技术可以推广到各种形式的懒惰求值。

注意:const不能(传递)应用到通过指针或者引用访问的对象。程序的读者可能会认为这种对象是”某种子对象“,但是编译期不能将这种指针或引用与其他指针或引用区分开来。即一个成员指针没有任何与其他指针不同的特殊语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值