读书笔记 ---- Const成员函数

本文探讨了C++中的Const成员函数,包括其目的、两种const概念——bitwise constness和logical constness,并通过示例说明如何在满足bitwise constness的同时实现logical constness。此外,还讲解了如何避免const和non-const成员函数的代码重复,提倡使用'概念上的常量性'编写更清晰的代码。
摘要由CSDN通过智能技术生成

前言

最近在读《Effective C++》,对里面的思想和代码深有感触,因此在此做点记录并加以自己的理解,方便以后查看。
本文内容来自条款03:尽可能使用Const (Use const whenever possible)

Const成员函数

介绍

将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。这一类成员函数之所以重要,基于以下两点:

  • 它们使class接口比较容易被理解:因为知道哪个函数可以改动对象而哪个函数不行,很是重要。
  • 它们使"操作const对象"成为可能:这对编写高效代码很关键,因为改善C++程序效率的一个根本方法就是以const引用(pass by reference-to-const)方式传递对象,而此技术的前提是我们有const成员函数可以用来处理取得的const对象。
Const成员函数的两种概念
  • bitwise constness:又称(Physical constness),即成员函数只有在不更改对象内的任何一个bit时,才叫const。这正是C++对常量性(constness)的定义,因此成员函数不可以更改对象内任何non-static成员变量。
  • logical constness:逻辑上的const,在某些情况下,const成员函数满足bitwise constness,但从逻辑上却是改变了对象的成员变量。因此这种情况导出了logical constness,这一派系主张,一个const成员函数可以修改它锁处理的对象内的某些bits,但只有在客户端侦测不出的情况写才能如此。
  • 示例:满足bitwise constness不满足logical constness的情况
    class CTextBlock{
    public:
    	//...
    	char& operator[] (std::size_t position) const{//bitwise const声明,但其实不满足逻辑上的const
    		return pText[position];//返回了指针所指向的数据引用,并不会改变指针本身,
    		//但却有可能改变指针指向的空间的数据,因此不满足逻辑上的const
    	}
    private:
    	char* pText;
    }
    
    int main(){
    	const CTextBlock cctb("Hello");
    	char *pc = &cctb[0];	//取的cctb[0]的引用,再获取地址
    	*pc = 'J';				//cctb现在变成了"Jello"
    	return 0;
    }
    
特性

两个成员函数如果只是常量性(constness)不同,是可以被重载的。具体请看示例。

示例1
问题

如何解决const成员函数的bitwise constness约束以实现logical constness?

情景

CTextBlock 可能高速缓存文本区块的长度以便应付查询

实现

因此我们可能写出这样的代码:

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函数内,不能修改对象数据,是要求bitwise const的
		lengthIsValid = true;
	}
	return textLength;
}

但这样length函数的实现并不是bitwise const,因为textLength和lengthIsValid都可能被修改,编译器不会通过。
解决办法:利用mutable(可变的)关键字释放掉non-static成员变量的bitwise constness约束。

/*
	情景:CTextBlock  可能高速缓存文本区块的长度以便应付查询
	功能:借用mutable实现logical const而非bitwise const
	mutable关键字:mutable是c++的一个与const相关的摆动场,意为可变的,
		因此mutable可以释放掉non-static成员变量的bitwise constness约束
	缺点:并不能解决所有的const难题
*/
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);
		lengthIsValid = true;
	}
	return textLength;
}
示例2
问题

在const和non-const成员函数中如何避免重复?

情景

TextBlock内的operator[]不单只返回一个reference to char,还会进行边界检测,访问记录,数据校验等。

实现
class TextBlock {
public:
	//...其他内容
	const char& operator[](std::size_t pos) const {
		//...边界检验
		//...记录数据访问(log)
		//...检验数据完整性
		return text[pos];//返回数据
	}
	//这样两个函数除了返回值得不同,没有什么区别,重复率太高
	char& operator[](std::size_t pos) {
		//...边界检验
		//...记录数据访问(log)
		//...检验数据完整性
		return text[pos];//返回数据
	}
private:
	std::string text;
};

这两个函数函数体一模一样,如何避免重复呢?可选的方法是:通过转型,将*this转为const去调用const的operator[]版本,并得到一个const reference to char,并去掉const约束后返回,因此得到以下代码:

class TextBlock {
public:
	//...其他内容
	const char& operator[](std::size_t pos) const {
		//...边界检验
		//...记录数据访问(log)
		//...检验数据完整性
		return text[pos];//返回数据
	}
	
	char& operator[](std::size_t pos) {
		return const_cast<char&>(
			static_cast<const TextBlock&>(*this)[pos]
			);
	}
private:
	std::string text;
};

注意: 此处不能反过来做,即不能在const的operator[]去掉*this的const约束再调用non-const 的 operator[]函数得到char&的返回值再转为const char&,const函数内部是不能对对象进行修改的。

总结
  • 编译器使用的常量性定义时bitwise constness的,但我们编写程序时,应该使用“概念上的常量性”。
  • 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。

参考文献

《Effective C++(第三版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值