Effective C++笔记
1.bitwise constness与logical constness
bitwise constness的主张是在const成员函数内,只有在这个成员函数不更改对象的任何成员变量(static)除外时在可以说是const。这正是c++对constness的定义,也是编译器的实现。
logical constness的主张是一个const成员函数可以修改它所处理对象内的某些成员变量,但只有在客户端侦测不出的情况才行。如果想要实现logical constness,可以使用mutable关键字声明成员变量,这样就可以释放掉non-static成员变量的witwise constness拘束,即在const成员函数内修改成员变量而编译器不报错。
另一种特殊情况,在类设计时同时为"const成员函数且返回值为const的引用类型" 与 "non-const成员函数且返回值为non-const的引用类型" 声明函数实现时,如果两个函数的仅有上述两点不同而代码相同,我们不应该分别实现这两个函数,即实现两次。为了避免其中的代码重复以及维护,修改时的巨大工作量,我们真正该做的是仅实现其中某一个函数,并使其中一个调用另一个,这促使我们将常量性移除(casting away constness)。在这个过程中,应使const成员函数实现出其non-const孪生兄弟,在non-const成员函数中调用const成员函数的方法类似以下语句:
return const_cast<char&>(static_cast<const Block&>(*this)[position]);
具体请见条款03:尽可能使用const
补充:const成员函数可以访问类的所有成员,并可以修改static类型的成员变量。
2.在类构造函数中使用成员列表初始化代替赋值动作
原因: c++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。
若采用在构造函数内为成员变量赋值,则实际运行时函数将首先调用成员变量的默认构造函数为其赋值,然后再立即对它们赋予新值。若采用成员列表初始化,则在初值列中针对各个成员变量而设的实参则被拿去作为各成员变量的构造函数的实参。在大型类的设计当中,赋值动作带来的内存损耗与效率问题将非常明显,
具体请见条款04:确定对象被使用前已先被初始化。
补充:常量与引用类型的成员变量的构造必须在成员列表初始化中实现。
3.若不想使用编译器自动生成的成员函数,应明确拒绝
原因:c++中,编译器可以暗自为类创建默认构造函数、复制构造函数、拷贝构造函数、拷贝构造操作符等。如果不想要错误的调用不需要的自动创建的函数,则需明确拒绝。
一个做法是在类中将其声明为private成员函数,且不需要定义其实现,这样就可以阻止编译器暗自创建其专属版本,并且由于其为private函数,若客户企图调用该函数则会在编译器报错,从而将连接期错误转移至编译期。
另一种做法是实现一个base class,在让我们的类继承该类。注意,这项技术可能会导致多重继承,具体请见条款06:若不想使用编译器自动生成的成员函数,就该明确拒绝。
class Uncopyable
{
protected:
Uncopyable() {};
~Uncopyable() {};
private:
Uncopyable(const Uncopyable&); //阻止copy
Uncopyable& operator=(const Uncopyalbe&); //阻止
};
4.以独立语句将new生成的对象存储入智能指针
原因:c++编译器在编译函数时,不像java和c#那样总是以特定次序完成函数参数的核算,也就是说当函数有多个参数时,其将会随机执行。
例如在在调用函数时写成如下所示,该语句虽然非常简单,仅有一行完成,但其背后蕴含着更为严重的问题。
processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());//错误!
//编译器可能的执行顺序:
//new Widget
//priority(),×这里如果对prio