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

C++编程规范之构造、析构与复制

2006-08-17 15:59 267 查看
构造、析构与复制
第47条:以同样的顺序定义和初始化成员变量
摘要:与编译器一致。成员变量初始化的顺序要与类定义中声明的顺序始终保持一致。
之所以说这一点,其实还是一个老问题,就是变量间相关依赖的问题。成员变量之间的依赖并不是个好现象,如果可以尽量减少这种依赖,如果出现循环依赖就不好了。我的建议是不要指望编译器首先会初始化哪个成员对象,这是我们所无法控制的。

第48条:在构造函数中用初始化代替赋值
摘要:用初始化列表代替构造函数内部赋值
class A

{
string m_strA;
A(){m_strA = “Hello” ;}

}
这样的写法means A():m_strA(){m_strA = “Hello” ;};相对而言当然A():m_strA(“Hello”){}的写法更好,将两步合并成一步。

第49条:避免在构造函数和析构函数中调用虚函数
摘要:虚函数仅仅“几乎”总是表现得很虚拟。
这个问题很简单,想想虚函数依赖什么?当然是对象中隐含的那个指向函数列表的指针vbptr。不要指望他在构造函数调用的时候就已经初始化完毕;同样不要指望它在析构函数调用的时候还生效。所以“几乎”就比较有意思,这意味着范围仅仅是构造完毕和析构之前。

第50条:将基类析构函数设置成公有且虚拟的,或者保护且非虚拟的
摘要:删除还是不删除,就是这个问题。
在设计基类的时候,我们就需要考虑,我们将希望派生类有什么样的行为?是否允许通过基类指针删除派生类指针?如果允许,需要将基类析构函数设置成公有且虚拟的;否则设置为保护且非虚拟的。

第51条:析构函数、释放和交换绝对不能失败
摘要:有些尝试必须是成功的。
析构函数不能出现异常的问题先前已经表述过了。在出现异常的时候,在清栈的过程中,系统会自动调用对象的析构函数。但是如果这个异常是析构函数跳出来的话,这就是比较苦恼的事情了,类似于“先有鸡还是先有蛋”的悖论就出现了。所以如果析构函数有出现异常的危险就自己catch住吧。编译器遇到这个问题其实就一刀切了,teminate。说到这里,想到了亚历山大大帝解绳结的办法。
同理,如果重载了delete的话,也需要注意同样的问题。至于Swap后面再说。

第52条:一致地进行复制和销毁
摘要:如果定义了复制构造函数、赋值操作符重载、或者析构函数,如果存在一个自定义的,那么往往需要自定义另外两个。
我记得我写过这个,但不记得是什么时候写的了,郁闷。

第53条:显式地启动或者禁止复制
摘要:清醒地进行复制:下面三种行为三选一:使用编译器提供的复制构造函数和赋值操作符;编写自己的版本;显式地禁止二者。
首先,赋值操作符和复制构造函数是应该成对出现的。其次,对于编译器自动生成的这二者都是位拷贝。对此要有清醒的认识,如果需要深拷贝的话就要自己想想办法了。Java就有所谓的clone,但对C++来说,编写自己的赋值操作符和复制构造函数,实现深拷贝就可以了。如果没有特殊需要就禁止吧,反正不是坏事。

第54条:避免切片。在基类中考虑用clone代替复制
摘要:切片的面包好,切片的对象可就不好了。
对象切片有可能无处不在,比如函数的值传递。这真可能成为很危险的事情。在这种情况下,禁止拷贝构造函数和赋值操作符可以避免这一点。如果需要复制的话,怎么办呢?显式的提供clone,学学Java是个好主意。在这种情况下,可以提供显式而虚拟的clone。
参照前面的设计原则,直接提供公有的虚拟Clone接口并不是一个好主意。更好的办法是提供公有的非虚拟Clone接口,而在接口内部调用私有的虚函数来执行这一动作。
防止对象切片的工作在于平时,对于对象之间的操作最好通过指针和引用,避免直接赋值。

第55条:使用赋值的标准形式
摘要:在使用operator=时,要使用具有特定签名的非虚拟形式。
遇到虚拟的operator=的时候,可能会遇到一些非常奇怪的情况。如果你在一个基类对象拷贝到一个派生类对象可能会比较麻烦。当operator=返回的是const类型,虽然有助于代码健壮性,但会被STL拒绝。
自定义operator=需要内部防止自我拷贝,不要期望别人在外部做这项工作。

第56条:只要可行,就提供不会失败的Swap(而且是正确的)
摘要:Swap既可能无关痛痒,也可能非常,要看你是否需要。但一旦提供的话,需要提供稳定、正确而强大的版本。
看看vector的swap所做的,
voidswap(_Myt& _X)
{
if (allocator == _X.allocator)
[align=left]{[/align]
[align=left]std::swap(_First, _X._First);[/align]
[align=left]std::swap(_Last, _X._Last);[/align]
[align=left] std::swap(_End, _X._End); [/align]
[align=left]}[/align]
[align=left] else[/align]
{_Myt_Ts = *this; *this = _X, _X = _Ts; }
}
对vector来说,如果分配器相同就使用std::swap,否则就用普通的交换。
[align=left]template<class_Ty> inline[/align]
[align=left] voidswap(_Ty& _X, _Ty& _Y)[/align]
[align=left] {_Ty_Tmp = _X;[/align]
_X = _Y, _Y = _Tmp; }
这是std::swap对他来说也只是最简单的交换而已,需要确保的就是operator=和复制构造函数的正确性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: