《Effective C++》读书笔记 1.让自己习惯C++

让自己习惯C++

条款01:视C++为一个语言联邦

将C++视为四个主要次语言的联邦

  • C
  • Object-Oriented C++
  • Template C++
  • STL

不同次语言的高效编程守则要求可能不同


条款02:尽量以const,enum,inline替代#define

define的坏处
不利于维护,安全性弱
1.用const取代

定义常量指针:注意const的放置

class专属常量:为确保常量至多只有一份实体,必须让它成为static成员:

class GamePlayer{
private:
	static const int NumTurns=5;//常量声明式
	...
};
//以上存在于头文件中
//以下存在于实现文件中
const int GamePlayer::NumTurns;//NumTurns的定义;

或者

class GamePlayer{
private:
	static const int NumTurns;//常量声明式
	...
};
//以上存在于头文件中
//以下存在于实现文件中
const int GamePlayer::NumTurns=5;//NumTurns的定义;

若它是个class专属常量又是static且为整数类型(integral type,例如int,char,bool),则在其不被后续操作取地址的情况下可以无须提供定义式

2.用enum取代
在class编译期间需要一个class常量值,例如

class GamePlayer{
private:
	enum { NumTurns=5};			//“the enum hack”
	int scores[NumTurns];		//使用该常量
};
//以上存在于头文件中
//以下存在于实现文件中
const int GamePlayer::NumTurns;//NumTurns的定义;

理论基础:一个属于枚举类型(enumerated type)的数值可权充int
与define相似的地方
无法取对应的地址(#define也得到,功能相似)
不想别人获得pointer或reference指向该资源

3.用inline取代

某些时候,宏定义的函数会导向奇怪的结果

//以a和b的较大值调用函数f
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
int a=5,b=0;
CALL_WiTH_MAX(++a,b);	//a被累加二次
CALL_WITH_MAX(++a,b+10);//a被累加一次

可以用template inline的函数取代;因为inline的函数也相当于内部直接插入一段代码

template<typename T>
inline void callWithMax(const T& a,const T& b)
{
	f(a>b?a:b);
}

总结:

对于单纯常量,最好以const对象或enums替换#defines

对于形如函数的宏,最好改用inline函数替换


条款03:尽可能使用const

1.const修饰指针时:
const出现在星号左边,表示被指物是常量;在右边表示指针自身是常量
const修饰迭代器时:

const iterator iter//类似T* const
const_iterator cIter//类似 const T*

2.将const实施于成员函数的好处
1.使class接口容易被理解,得知哪些函数可以修改对象内容,哪些不行。
2.pass by reference-to-const方式传递对象需要有const成员函数可用来处理得到(并经修饰而成)的const对象。
两个成员函数如果只是常量性不同,可以被重载

3.bitwise constness(编译器就是这种)和logical constness

bitwise constness是成员函数只有在不更改对象的任何成员变量时就认为是符合const,这个定义存在一定不足,因为即使编译器查不出更改也可能存在更改,比如改变指针所指

logical constness是一个const成员可以修改它所处对象内的某些bits,但只有在客户端侦测不出的情况下才可以

通过mutable关键字,可以释放掉non-static成员变量的bitwise constness约束,从而在const函数内也可以修改所在对象的mutable成员变量

mutable bool lengthIsValid;//会被更改,即使在const成员函数内

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

实现常量性转除。消除const的性质,具体的做法是在non-const的函数里调用对应的const函数,并实现强制转换(const_cast可以强制消除const属性,static_cast可以实现强制转换)

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

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

当const和non-const成员函数存在等价实现,用non-const函数调用const版本可以避免代码重复


条款04:确定对象被使用前已被初始化

对于无任何成员的内置类型,需要手工初始化

构造函数最好使用初始化列表,而不要在构造函数内赋值操作。初值列列出的成语变量,其排列次序应该和它们在class中的声明次序相同

要区分赋值和初始化,对于class的对象,在构造函数内赋值实际上是先调用了它们的默认构造函数,然后做赋值操作,比较低效

成员的初始化列表本质是调用复制构造函数,效率较高

规则: 总是在初值列中列出所有成员变量,以免还需记住那些成员变量无需初值

为了免除“跨编译单元之初始化次序”的问题(就是定义文件之间的初始化没有先后顺序),应该以local static对象替换non-local static对象

non-local static:函数外的static对象就是non-local static变量

问题:C++对于不同编译单元内的non-local static对象的初始化次序没有明确定义

解决:用singleton的思想。将每个non-local static变量搬到专属函数内,这些函数返回一个reference指向它所含的对象,然后用户调用这些函数,不直接调用对象。这时,non-local static变成了local static

这种reference-returning函数的形式:定义并初始化一个local static, 然后返回它

class FileSystem{
...
};
extern FileSystem tfs;
//替换如下
FileSystem& tfs()
{
	static FileSystem fs;
	return fs;
}

总结:

无任何成员的内置类型,需要手工初始化

构造函数最好使用初始化列表,且列出的成员变量次序应该和它们在class中的声明次序相同

为免除“跨编译单元之初始化次序”问题,请以local static 对象替换 non-local static对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值