这里是第二部分
条款 5:C++默默编写并调用哪些函数
如果你没有写构造函数、析构函数、copy构造函数和赋值操作符,C++会自己为你编写默认的函数。如果你自己编写了,C++不再合成
如果你打算在一个内含 reference成员的class内支持赋值操作,你必须自己定义赋值操作符,面对const成员,编译器的反应也是一样的。如果某个基类的copy 操作符是一个private,那么编译器拒绝为其派生类生成一个。
条款 6:若不想使用编译器自动生成的函数,就应该明确拒绝
常用的方法是将copy函数(copy构造函数和copy赋值操作符)声明为private而不去定义,但是这么做类的友元函数还是可以访问。友元函数的访问会导致连接期间的错误,我们应该把连接期间的错误提到编译期间:
class A
{
protected:
A();
~A();
private:
A(const A&);
A& operator=(const A&);
};
class B : private A
{
.....
};
条款 7:virtual析构函数
这里主要是说具有多态性质的基类的析构函数应该声明为virtual,不然的话析构的时候如果是析构一个基类指针那么派生类部分就无法析构了。但是不是所有的类的析构函数都应该声明为virtual,因为如果没有多态性质那么程序会无缘无故的占用一个指针vtbl即虚函数表指针,造成资源的浪费。
那么换句话说,不带virtual的基类一般不能够被继承
条款 8 :不要让异常逃离析构函数
一般来说析构函数不要抛出异常。如果抛出异常,我们应该捕捉异常,吞下它或者结束程序:
DB::~DB()
{
try
{ db.close();}
catch(....){std::abort(); }
}
如果客户需要对某个运行期间的异常做出反应,那么class应该提供一个普通函数执行该操作:
class DB
{
public:
....
void close()
{
db.close();
closed = true;
}
~DB()
{
if(!closed)
{
try{db.close();} catch(....){....................}
}
}
private:
DBConnection db;
bool closed;
};
条款 9: 不要在析构函数和构造函数中调用virtual函数
构造函数的构造顺序是先基类后派生类,在基类构造的时候virtual函数不是virtual函数,因为此时派生类还没有构造出来。析构函数的析构与构造的次序相反,所以不能够出现多态行为。一种可行的方法:
class A
{
public:
explicit A(const std::string& logInfo);
void log(const std::string& logInfo) const; //non-virtual
....
} ;
A::A()
{
log(logInfo); //non-virtual调用
}
class B : public A
{
public:
B(param):A(createLogString(param)) //将log消息传给基类
{ ... }
....
private:
static std::string createLogString(param);
};
条款10 : operator=返回一个*this
条款11: operator=中处理自我复制
例子;
A& A::operator=(const A& rhs)
{
if(this==&rhs) return *this;
delete pb; //pb 是class A的一个成员private: Bitmap* pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这样做是可以的,但是不具有安全性
A& A::operator=(const A& rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
一种替代的方法:class A
{
...
void swap(A& rhs);
};
A& A::operator=(const A& rhs)
{
A temp(rhs);
swap(temp);
return *this;
}
条款12: 复制对象不要忘记每一个成分的复制
派生类的复制的时候不要忘记对基类的复制
不要以某个copy函数实现另外一个copy函数。应该将共同的机能放进第三个函数中,由两个copy函数共同调用