Effective C++函数编译器
1:编译器可以暗自为class创建 default构造函数、copy构造函数、copy assignment操作符、析构函数。 唯有这些函数被需要调用,他们才会被编译器创建出来。
2:如果你打算在一个“内含引用成员”或者“内含const成员”的class内支持赋值操作,你必须自己定义copy assignment操作符。
3:如果base classes将copy assignment操作符声明为private,编译器将拒绝为子类生成copy assignment操作符。
4:编译器产生的析构函数是个non-virtual函数。
P6:若不想使用编译器自动生成的函数,就该明确拒绝1:为驳回编译器自动提供的功能,可以将相应的成员函数声明为private并且不予实现。或者使用Uncopyable这样的base class方法。
class Uncopyable{ protected: //允许derived对象构造和析构 Uncopyable(){} ~Uncopyable(){} private: //但是阻止copying Uncopyable( const Uncopyable& ); Uncopyable& operator=(const Uncopyable&); };P7:为多态基类声明 virtual 析构函数
1:如果析构函数不是virtual,则当子类对象是经由一个base class指针析构的时候,他的derived部分会没被销毁。
2:多态性质的base class应该声明一个virtual析构函数。如果class带有virtual函数,它就应该有一个virtual析构函数。
3:class设计的目的如果不是作为base class使用,或不是为了具备多态性,就不该声明virtual析构函数。 STL容器vector,list,set,string等都是non-virtual析构。
4:具有多态性的base class的设计目的是为了用来“通过base class接口处理derive对象”。
5:析构函数的运作方式是,最深层派生的那个class其析构函数最先被调用,然后是每个base class的析构函数被调用。
6:抽象类的virtual析构函数应该有一份定义,原因是第5条在子类的析构函数中会调用抽象类的析构函数。
virtual ~AWOW()=0; //声明抽象析构函数 AWOW::~AWOW()(){} //抽象析构函数的定义P8:别让异常逃离析构函数
1:析构函数如果可能抛出异常,应该捕获异常。并且提供一个普通函数给用户机会自己处理该操作。
class DBConn{ public: void close() //给客户端一个机会处理可能导致异常的行为 { db.close(); closed = true; } ~DBConn() { if(!closed) { try{ db.close(); } catch(...){ //记录下来并结束程序或吞下异常不做处理。 } } } private: DBConnection db; bool closed; };P9:绝不在构造和析构过程中调用virtual函数
1:在deviced class对象的base class构造期间,对象的类型还只是base clas对象而不是deviced class对象! virtual函数只会被解析到base class,对象在deviced class的构造函数开始之前不会成为一个deviced class对象。析构函数同样的道理,只是析构顺序自deviced class到base class。
2:可以让deviced class将必要的信息向上传递至base class构造函数去调用non-virtual函数。
P10:令operator= 返回一个 reference to *this1:令赋值操作符返回一个reference to *this,这样可以实现连锁赋值。如 x=y=z=“str”;
P11:在operator= 中处理自我赋值1:使用“证同测试”、语句顺序调整、copy-and-swap等方式确保operator=的自我赋值是正确的。
2:确保然后函数如果操作多个对象时,其中有多个对象是同一个对象时,其行为仍然正确。
class Widget{ void swap(Widget& rhs); //交换*this和rhs的数据,详见P29。 BitMap* pb; }; Widget& Widget::operator=(const Widget& rhs) { if(this == &rhs) return *this; //证同测试 Widget temp(rhs); //为rhs数据制作一份副本 swap(temp); //将*this与副本数据交换;copy-and-swap技术 return *this; }
Widget& Widget::operator=(const Widget& rhs) { BitMap* pOrig = pb; pb = new BitMap(*rhs.pb); delete pOrig; //这么写的目的是我们需要注意在复制pb前不能删除pb。 return *this; }P12:复制对象时勿忘其每一个成分
1:当编写copying函数时,应确保复制“对象内的所有成员变量”,确保调用所有base class内适当的copying函数复制”所有base class成分”。
2:不要以某个copying函数去实现另一个copying函数,应该把功能放在第三个init函数中去由copying函数调用。
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs) :Customer(rhs) //调用base class的copy构造函数 { logCall("拷贝构造函数"); } PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs) { logCall("复制操作符"); Customer::operator=(rhs); //对base class成分进行赋值操作 priority = rhs.priority; retern *this; }