首页 > 代码库 > 《Effective C++ 》学习笔记——条款03

《Effective C++ 》学习笔记——条款03

***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************


 一、 Accustoming Yourself to C++


Rule 03: Use const whenever possible.

条款03:尽可能的使用const


const就是常量,它允许你指定一个语义约束,编译器会强制实施这项约束。

多才多艺的关键字const 有多种用途:

① 在classes 外部修饰global 或 namespace 作用域中的常量

② 修饰文件、函数 或 区块作用域(block scope )中被声明为static的对象

③ 用它修饰classes内部的 static 和 non-static 成员变量


几个重要的作用

1.对于指针

对于指针,可以设定 指针自身 、 指针所指物 或 两者都是(或都不是) const

分辨方法也很简单:如果const出现在星号(*)左面,表示被指针所指的物是常量,如果出现在右面,表示指针自身是常量。

自此,也可以扩展到 迭代器 ( iterator )

如果你想表示这个迭代器不得指向不同的东西,但它所指的东西的内容是可以改动的:

const std::vector<int>::iterator iter
反之,迭代器可以指向不同的东西,但所指的内容不可改动:

std::vector<int>::const_iterator iter

2.最具威力的用法——对于函数声明时的应用

在一个函数声明中,const 可以和 函数返回值、各参数、函数自身 产生关联。

①函数的返回值

令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。

②函数的参数

除非你有需要改动参数或local const对象,否则请将它们声明为const。仅仅几个字符,可以帮你省下很多的烦恼。

③函数自身(const成员函数)

const实施于成员函数的理由有两个:

<1> 它们使classes接口比较容易理解(可以明确知道哪个函数可以改动对象内容,而哪个不能)

<2> 它们使“操作const对象”成为可能。 这个是 "pass by reference to const 方式传递对象"技术的基础。

而且,在重载方面,还有一个很重要的特性。

对于一个类,两个函数:

class TextBlock
{
public:
    ....
    const char& operator[](std::size_t position)  const
    {  return text[position];  }
     char& operator[] ( std::size_t position )
    {  return text[position];  }
private:
    std::string text;
};

只要重载 operator[] 并对不同的版本给予不同的返回类型,就可以令 const 和 non-const TextBlocks 获得不同的处理:

std::cout<<tb[0];   // 没问题,读一个non-const
tb[0] = ' x ';    // 没问题,写一个non-const
std::cout<<ctb[0];    // 没问题,读一个const
ctb[0] = ' x ';    // 错误,写一个const

错误的原因是  企图对一个“由const版之operator[]返回”的const char& 施行赋值动作。

此处要注意一下 non-const operator[] 的返回类型是个 reference to char (引用char型),不是char,

否则 tb[0] = ‘ x ‘将无法通过编译。


对于 成员函数如果是const意味性 有两个流派:bitwise 和 logical

——bitwise const 阵营

奉行: 成员函数只有在不更改对象之任何成员变量(static除外)时才可以说是const。也就是说它不更改对象内的任何一个bit(位)。

优点:很容易侦测违反点,编译器只需要寻找成员变量的赋值动作即可。

不足:不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试。

可以看下面这个例子:

const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J';

这样cctb现在存储的是 "Jello"这个内容,显然不符合const的定义


——logical const 阵营

奉行:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端监测不出的情况下才如此。

例如 你的CTextBlock class 有可能高速缓存(cache)文本区块的长度以便询问:

class CTextBlock
{
public:
    ...
    std::size_t length() const;
private:
    char* pText;
    std::size_t textLength;<span style="white-space:pre">	</span>// 最近一次计算的文本区块长度。
    bool lengthIsValid;<span style="white-space:pre">		</span>// 目前长度是否有效
};
std::size_t CTextBlock::length() const
{
    if( !lengthIsValid)
   {
        textLength = std::strlen( pText );<span style="white-space:pre">	</span>// 错误!在const 成员函数内不能赋值给这俩者
        lengthIsValid = true;
    }
    return textLength;
}

length实现肯定不是 bitwise const ,因为textLength 和 lengthIsValid都可能被修改。它们两者被修改对const CTextBlock 对象而言虽然可以接受,但编译器不同意。

所以就用一个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;		// const成员函数内也可更改
};

std::size_t CTextBlock::length() const
{
    if( !lengthIsValid)
   {
        textLength = std::strlen( pText );	// 现在可以这样
        lengthIsValid = true;
    }
    return textLength;
}

So 在const 和 non-const成员函数中避免重复

这个问题是由上面问题产生的,mutable固然可以解决变量的问题,但是如果这个东西很长,

我们则需要些两份很长很长的怪物,so scary!

这就需要non-const来调用const的东西来避免重复,这样改动也方便一些,

{

为什么不让const调用non-const的?

拜托,const成员函数承诺绝不改变其对象的逻辑状态,non-const没有

}


这样就需要 调用 转型 这个概念,看下面的例子:

class TextBlock
{
public:
	...
	const char& operator[](std::size_t position) const
	{
	...
	...
	...
	return text[position];
	}
	char& operator[](std::size_t position)
	{
		return
			const_cast<char&>(
			static_cast<const TextBlock&>(*this) [position]
			);
	}
	...
};

这里用到了两个转型:

static_cast     将non-const对象转换成const对象

const_cast     移除const


结束语:

const是一个非常奇妙的且非比寻常的东西,

它可以用在 指针和迭代器上;

在指针、迭代器及reference指涉的对象身上;

在函数参数和返回类型上;

在local变量身上; 

在成员函数身上,

等等。。。。


Please remember:

<1> 将某些东西声明为 const 可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。

<2> 编译器强制实施bitwise constness,但你编写程序时应该使用 conceptual constness(概念上的常量性)。

<3> 当const和non-const 成员函数有着实质等价的实现时,令non-const版本用const版本可避免代码重复。



***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************

《Effective C++ 》学习笔记——条款03