《effective C++》读书笔记(一)

来源:互联网 发布:abb工业机器人软件 编辑:程序博客网 时间:2024/05/05 13:09

1.类中遇到需要使用#define的地方要尽量避免。

1)       在类中避免

#define NUM 10

int array[NUM];

使用枚举变量解决,即enumhack。

2)       在类中避免

#define MAX(a,b) f((a) >(b) ? (a) : (b))

使用templateinline解决

template <typename T>

inline void max(const T&a, const T& b)

{

  f(a > b ? a : b);

}

这样不仅获得了宏一样的效率而且拥有具有和一般函数一样的可预料行为和类型安全性。而且max是个真正的函数它遵守作用域和访问规则。

参考:《effective c++》p15,clause2

 

2.迭代器看成是指向类的指针。const迭代器是迭代器本身指向不能改变,const_iterator是该迭代器指向的内容不能改变。

 

3.const的使用。

1)       函数声明式中使用const主要是为了避免无意的错误。

2)       函数的返回值如果不需要改变应该定义为const,如果需要改变应该定义为引用或指针(可能破坏封装性)。

3)       const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;

4)       const对象只能调用const成员函数,不能调用非const成员函数。    

5)       const成员函数能被非const对象使用,也能被非const成员函数所调用。

6)       const成员函数只能调用const成员函数,即不能调用非const成员函数。(既然希望该函数不修改成员变量,那么他所调用的函数自然应该也是const的)

7)       作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。

8)       const成员函数可以访问并修改类的非conststatic成员变量。(const对象是对象自身不能改变,而static是类公有的)

9)       。如果需要编写类中成员的函数的const版本和非const版本,可以通过转型(casting),可以运用函数的const版本实现出非const版本的方式简化代码。(详见《effective c++》p24)

 

4.关于类的初始化

1)       创建对象的时候初始化。

2)       C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。所以要在类的构造函数中使用初始化成员列表的方式初始化类成员,在构造函数内部对成员变量的操作都是赋值操作。

3)       规定总是在初始化列表中列出所有成员变量。以免还得记住哪些成员变量(以免他们在成员初始化列表中被遗漏的话)可以无需初值。

参考:《effectivec++》clause4

 

5.关于non-local static对象的初始化。

1)       所谓static对象,其寿命从被构造一直到程序结束为止。

2)       函数内的static对象称为local static对象。

3)       global对象、定义于namespace作用域内的对象、在class内或在文件作用域内的对象称为non-local static对象。

4)       编译单元:指产出单一目标文件的哪些源码。基本上它是单一源码文件加上其所包含的头文件。

5)       如果某个编译单元的non-local static对象的初始化动作使用了另一个编译单元的non-local static对象,那么将会产生未定义行为。因为无法保证使用到的non-local static对象已经初始化了。

6)       解决5)中的问题的方法是使用reference-return函数。即将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向这个对象。即,将non-local static对象转换为local对象。

7)       这种方法还可以减少全局变量的个数,使对变量的使用可以监控。

例:ob源码中使用了这种转换方式。(虽然不是为了解决初始化的未定义行为问题,因为没有使用其他static变量)

 

注:任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某件事情发生”都会有麻烦。解决方法是在单线程启动阶段手工调用所有的reference-return函数避免race condition。

参考:《effectivec++》clause4

 

6.如果你声明了自己构造函数,那么编译器就不会再创建一个默认构造函数。

 

7.如果一个类作为基类,并且定义了virtual函数,那么必须定义virtual析构。但    virtual函数会增加开销。

classes的设计目的如果不是作为base class使用,或不是为了具备多态性,就不应该声明virtual析构函数。

参考:《effectivec++》clause7

 

8.在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class。

 

9.在定义operator=时如果允许出现x=y=z这种连锁赋值的形式,就需要令operator=返回一个reference to *this。否则将operator=的返回值设置为void,即禁止这种连锁赋值操作。

 

10.在operator=中处理“自我赋值”

1)       对指针进行自我赋值可能存在自我赋值的安全性问题。(同一个对象delete两次)

参考:《effectivec++》clause11

2)       赋值的效率问题。(自我赋值出现的频率很少)

    
例:OB中

其中OB_LIKELY(this!=&other)就是证同测试。目的是为了提高效率。

其中#defineOB_LIKELY(x)      __builtin_expect(!!(x),1)

__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间乱费。

参考:http://blog.csdn.net/sunnybeike/article/details/6802579

0 0
原创粉丝点击