二.构造/析构/赋值运算

来源:互联网 发布:vivo网络制式怎么查看 编辑:程序博客网 时间:2024/05/18 17:59

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

如果写一个空类:

class Empty{};

会被编译器处理成:

class Empty

{
public:
    Empty()
    {


    }


    ~Empty()
    {


    }


    Empty( const Empty& rhs )
    {


    }


    Empty& operator=( const Empty& rhs )
    {


    }
};

这些函数都是public且为inline,唯当这些函数需要被调用,它们才会被编译器创建出来。

Empty e1; //default构造函数

Empty e2( e1 ); //copy构造函数

e2 = e1;//拷贝运算符

default构造函数和析构函数主要是给编译器一个地方放置幕后代码,像是调用base class和non-static成员变量的构造函数和析构函数。注意,编译器产出的析构函数式一个non-virutal,除非这个class的base class自身声明有virtual析构函数(这种情况下这个函数的虚属性主要来自于base calss)

至于copy构造函数和copy assignment操作符,编译器创建的版本只是单纯的将来源对象的每一个non-static成员变量拷贝到目标对象。

如果打算在一个内含ref成员的class内支撑赋值操作,则必须自己定义赋值操作符。面对内含const成员的class,编译器也会拒绝赋值操作。还有一种情况,如果某个base class将copy assignment操作符声明为一个private,编译器将拒绝为其derived classes生产一个copy assignment操作符,毕竟编译器为derived classes所生产的copy assignment操作符想象中可以处理base classes部分,但它们当然无法调用derived classes无权调用的成员函数。


请记住:

1.编译器可以为class创建default,copy构造函数copy assignment操作符,析构函数。



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

明确声明一个private版本的,这样就阻止了编译器暗自创建其专属版本,同时也阻止了人们调用它们。

一般而言这样是不安全的,因为成员呢函数和friend函数还是可以调用private函数。除非你够聪明不去定义它们,那么如果某些人不慎调用任何一个,会获得一个链接错误。这一伎俩在C++ Iostream程序库中阻止copying行为。

将连接器错误移植到编译器是可能,仅仅需要专门设计一个base class。

class UnCopyable
{
protected:
    UnCopyable();
    ~UnCopyable();  //允许构造和析构


private:
    UnCopyable( const UnCopyable& );
    UnCopyable& operator=( const UnCopyable& ); //阻止copy
};

为了阻止拷贝,仅仅继承UnCopyable就行了

class HomeForSale : private UnCopyable
{


};

也可以使用Boost提供的版本,class名为noncopyable。


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

C++指出,当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义----实际执行时通常发生的是对象的derived成分没有被销毁。而其base部分销毁了。

于是在工厂模式中单纯的通过删除base指针会造成一个局部销毁,这可能是形成资源泄露,败坏的数据结构以及在调试器上浪费时间的途径。

消除这个问题的做法:给base class一个virtual析构函数,此后删除derived class对象就会销毁整个对象,包括derived class的成分。

同时,任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数。

如果class不含virtual函数,通常表示它并不试图被用作一个base class.当一个class不企图被作为base class,令其析构函数为virtual往往是一个馊主意。

欲实现virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数该被调用。这份信息通常是由一个vptr指针指出,vptr指向一个函数指针构成的数组,称为vtbl。每一个带有virtual函数的class都有一个相应的vtbl。当对象调用某一virtual函数,实际被调用的函数取决于该对象的vptr所指的那个vtbl------编译器在其中寻找适当的函数指针。

这样使用virtual函数的class会增加对象的体积,且其对象也不再和其他语言(C)内的相同声明有着一样的结构,因此就不再可能把它传递到其他语言所写的函数中,除非你明确补偿vptr------那属于实现细节,也因此不再具有移植性。

只有当class内含有至少一个virtual函数,才为它声明virtual析构函数。


即使class完全不带virtual函数,被non-virtual析构函数问题给咬伤还是有可能。

class SpecialString: public std::string//string有个non-virtual析构函数

{

};

SpecialString* pss = new SpecialString( "Hello" );

string* ps;

ps = pss;

delete ps;//未有定义,*ps中的specialString会资源泄露,因为SpecialString析构函数未被调用。

相同的分析适用于任何不带virtual析构函数的class,包括所有的STL容器。所以,企图继承一个标准容器或任何其他带有non-virtual函数的Class,拒绝诱惑吧。(很不幸,C++没提供类似java的final class或C#的sealed class那样的禁制派生机制。)


有时候令class带一个pure virtual析构函数,可能颇为便利。

pure virutal函数导致abstract class-------也就是不能被实体化,也就是说不能为那种类型创建对象。然而有时候你希望拥有抽象class,但手上没有任何pure virtual没怎办?由于抽象类总是企图被当做一个base class来用,而又由于base class应该有个virtual析构函数,并且由于pure virtual函数会导致抽象class,因此解法很简单:为你希望它成为抽象的那个class声明一个pure virtual析构函数。且必须为这个pure virtual析构函数提供一份定义。

析构函数的运作方式为:最深层派生的那个class其析构函数祖先被调用,然后其每一个base class的析构函数被调用。


给base class一个virtual析构函数,这个规则只适用于polymorphic base class身上。这种base class的设计目的是为了用来通过base class接口处理derived class对象。并非所有的base class的设计目的都是为了多态用途,因此它们不需要virtual 析构函数。


请记住:

1.polymorphic base class应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该有一个virtual析构函数。

2..class的设计目的如果不是作为base class使用,或不是为了具备多态性,就不应该声明为virtual 析构函数。



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

C++并不禁止析构函数吐出异常,但它不鼓励你这样做:




原创粉丝点击