您的位置:首页 > 编程语言 > C语言/C++

effective c++读书笔记二——构造/析构/赋值运算

2010-10-18 22:18 169 查看

4. 条款5:了解C++默默编写并调用哪些函数

声明了一个构造函数,编译器将不再为它创建default构造函数。

5. 条款6:若不想使用编译器自动生成的函数,就该明确拒绝

如果你不希望某个class支持某一特定功能函数,比如不希望某个类具有copy构造函数和copy assigment函数,你不声明,但是编译器会为你默认生成,若你不需要对应的函数,请明确拒绝。
l 将对应的函数声明为private,明确告诉编译器阻止生成对应的函数。
l 比较常用的方法,采用继承机制,将父类对应的两个函数声明为private,这个在boost有对应的类。

6. 条款7:为多态基类声明virtual析构函数

(1)当derived class对象经由一个base class指针被删除,而该base class带一个non-vritual析构函数,其结果——实际执行时通常发生的是对象的derived成分没被销毁。会造成一个“局部销毁”。
(2)只有当class 内含至少含有一个virtual函数才为它声明virtual析构函数,并且该bases class是一个带多态性质的。无端将所有classes的析构函数声明为virtual,就像从未声明一样是错误的。
(3)classes设计目的不是作为base classes使用,不是为了具备多态性质,比如标准的string和STL容器都不被设计为base classes使用,有些classes设计目的是作为base classes但是并非为了多态用途,比如uncopyable函数。这些不能将构造函数声明为virtual。

7 条款08 别让异常逃离析构函数

(1)析构函数绝对不要吐出异常。记住例子std::vector<Widget> v;假设v中第1个元素吐出异常,而其他元素也应该销毁,否则会发生资源泄露。假设第二个widget析构函数也发生异常,两个异常同时存在,程序若不是结束执行就是导致不明确行为。
(2)若析构函数必须执行一个动作,而该动作有可能会失败抛出异常,该怎么办?
比如一个class负责数据库连接,析构函数中调用close()关闭数据库连接的函数。
有两种解决办法,但是这两种解决办法都不是最好的办法,因为他们都无法对“导致close抛出的异常”的情况做出反应:
l Close函数抛出异常就结束程序,通常可以用abort完成。
DBConn::~DBConn()
{
try{db.close(); }
catch (CException* e){
//制作运转记录,记下对close的调用失败;
std::abort();
}
}
l 吞下调用close而发生的异常:
DBConn::~DBConn()
{
try{db.close(); }
catch (CException* e){
//制作运转记录,记下对close的调用失败;
}
}
最好的解决办法:
class DBConn{
public:
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if(!closed){
try{db.close(); }
catch (CException* e){
//制作运转记录,记下对close的调用失败;
}
}
}
private:
bool closed;
}
如果客户需要对某个操作函数运行期间抛出异常做出反应,提供一个普通函数让用户调用。

8.条款09:绝不在构造和析构函数过程中调用virtual函数

(1)base class构造函数期间virtual函数绝不会下降到derived class阶层。Derived class的base class构造期间,对象的类型是base class而不是derived class。相同的道理也适用于析构函数,derived class析构函数开始执行,对象的derived class成分就呈现未被定义的状态,如果base class中调用,就成为一个base class对象。
(2)确定你的构造函数和析构函数都没有调用virtual函数。
(3)解决办法:无法使用virtual函数从base classes向下调用,在构造函数期间,你可以藉由“令derived classes将必要的构造信息向上传递至base class构造函数”替换加以弥补。

9.条款10:令operator =返回一个reference to* this

为了实现连锁赋值形式:如:x = y = z = 10; 赋值操作符必须返回一个reference指向操作符的左侧实参。
如下:
class Widget{
public:
......
Widget& operator =(const Widget& rhs){
......
return* this;
}
......
};

9 条款11:在operator=中处理自我赋值

(1)首先不处理自我赋值会发生的异常分析。如果不处理自我赋值问题是,operator =函数内的*this 和rhs为同一个对象,当用析构时会出现delete会删除一个已经销毁的对象。
(2)需要采用证同测试,传统的做法是采取检测是否为自我赋值,加入如下判断:
if(this ==&rhs) return *this;
delete pb;
pb = new Bitmap(*this.pb);
return * this;
但是这种方式,不具有异常安全,如果new导致异常
(3)具备异常安全的做法,是先记住原来内容,再new一个原内容的复件,删除原先的指针。具体做法如下:
bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return * this;
此办法具有异常安全,但是缺点效率不高。
(4) 最好做法,采用copy and swap的技术。
widget temp(rhs);
swap(tmep);
return *this;

10 .条款12:复制对象时勿忘其每一个成分

(1)当你为derived class撰写copying函数时,必须小心的复制base class成分,那些成分往往是private的,无法直接访问,让derived class的copying函数调用相应的base class函数。
(2)copy assignment操作符调用copy构造函数不合理的,反过来调用也是不合理的。如果两个函数有相似的代码,正确的做法是将相同的代码建立一个新的函数给两者调用,一般并声明为private并命名为init。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: