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

《Effective C++》学习笔记(三)

2014-08-25 15:26 344 查看
原创文章,转载请注明出处:/article/8316762.html

闲谈

从北戴河旅游归来,该收心继续上班了,接下来将继续学习《Effective C++》,今天看的是构造/析构/赋值运算部分。

条款05:了解C++默默编写并调用那些函数

当经过编译器处理后,并没有绝对的空类,例如:
class Empty{ };
编译器会为它声明一个default构造函数,一个copy构造函数、一个copy assignment操作符,一个析构函数:
class Empty
{
public:
Empty() { ... };
Empty(const Empty &rhs) { ... };
~Empty() { ... };

Empty& operator=(const Empty &rhs) { ... };
}
注意!!!仅仅是被声明出来了,只有当这些函数被真正调用的时候,它们才会被编译器创建出来。
一般情况下编译器产出的析构函数是non-virtual的,但如果这个类的基类(base class)自身声明有virtual析构函数,那就另当别论了。
如果在类中有任意构造函数,编译器不会再为它创建default构造函数。

总结:
编译器可以暗自为class创建default构造函数、copy函数、copy assignment操作符,以及析构函数。

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

世界上没有两片完全相同的叶子,所以当我们声明一个叶子对象,并希望通过这个叶子对象拷贝出另一片叶子,这个行为是不合法的。
Leaf l1;
Leaf l2;
Leaf l3(l1); // 企图拷贝l1,不应该通过编译
l3 = l2; // 企图拷贝l2,也不应该通过编译
为了使这个机能实现,阻止编译器自动生成,我们可以将copy构造函数和copy assignment操作符定义为private。
但这样并不是绝对的安全,因为成员函数和友元函数依旧可以调用它,当然,依旧有法可依。
如果我们只声明它们,不去定义,那么如果别人调用它们将会获得一个连接错误。
class Leaf
{
public:
...
private:
...
Leaf(const Leaf&);
Leaf& operator=(const Leaf&);
}

当然,这样我们横跨了编译期和连接期,想要将连接期报错提前到编译期也是可以的。

class Uncopyable
{
protected:
Uncopyable() {} //允许派生对象构造和析构
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&); //但阻止copying
Uncopyable& operator=(const Uncopyable&);
}
class Leaf: private Uncopyable
{
...
}

Uncopyable的实现与运用颇为微妙,包括不一定需要以public继承它,以及Uncopyable的析构函数不一定得是virtual等等,但是这样将有可能造成多重继承,而多重继承有时会阻止空白基类最优化(empty base class optimization)。
当然,到目前为止,所讲得内容仅仅止步于c++11发布前,C++11发布后为拒绝编译器的“好意”提供了新的方法:
class Leaf
{
public:
...
private:
...
Leaf(const Leaf&) = delete;
Leaf& operator=(const Leaf&) = delete;
}


总结:
1)为驳回编译器自动提供的机能,可以将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的base class也是一种做法。
2)支持C++11的编译器上也可以将成员函数定义成delete。

条款07:为多态基类声明virtual析构函数

当一个对象定义在堆中,同时它拥有一个有着non-virtual析构函数的基类,当这个对象被delete的时候,特属于派生类(derived class)的部分并没有被销毁,于是会照成局部销毁的现象。
从上一段话的关键字中不难知道,解决办法便是给base class一个virtual析构函数,virtual析构函数的目的是允许derived class的实现得以客制化。任何class只要带virtual函数都几乎确定它也应该有一个virtual的析构函数。
如果claa不含virtual函数,通常表示它并不会被用作一个基类。当class不起图呗当做基类,令其析构函数为virtual是个馊主意。
virtual函数之所以能实现多态,决定什么时候调用哪一个virtual函数,是因为它携带着一份特殊信息,由一个所谓的vptr(virtual table pointer)指出。vptr指向一个由函数指针数组构成的数组,称作vtbl(virtual table),每一个带有virtual函数

总结:
1)带多态性质的(polymorphic)基类(base class)应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该拥有一个virtual析构函数。
2)Classes的设计目的如果不是作为基类使用,或不是为了具备多态性,就不该声明virtual析构函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: