• More Effective C++ 条款25 将constructor和non-member function虚化


    1. Virtual constructor

        原则上构造函数不能为虚:虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体,但构造函数用于构造对象,在对象构造之前自然没有动态类型的概念,虚与非虚也就无从谈起.所谓的的virtual-constructor实际上是"仿virtual-constructor",它本质上不是constructor,但能够产生不同类型的对象,从而实现"virtual-constructor"的功能,考虑以下继承层次用于管理新闻报道:

    class NLComponent { //抽象基类,用于实时通讯的组件,其中内含至少一个纯虚函数
    public:    
        ...
    };
    class TextBlock: public NLComponent {
    public:
        ... // 没有内含任何纯虚函数
    };
    class Graphic: public NLComponent {
    public:
        ... // 没有内含任何纯虚函数
    };
    class NewsLetter { //一份实时通信是由一系列NLCompnent对象组成
    public: 
        ...
    private:
        list<NLComponent*> components;
    };
    View Code

        NewsLetter可能存储于磁盘中,为了从磁盘中读取NewsLetter对象到内存中,它可能有一个接受istream&参数的构造函数:

    class NewsLetter {
    public:
        NewsLetter(istream& str);
        ...
    private:
        list<NLCompnent*> compnents;
        ...
    };
    NewsLetter::NewsLetter(istream& str){
        while (str) {
            read the next compnent object from str;
            add the object to the list of this;
            newsletter's compnents;
        }
    }
    View Code

        NewsLetter(istream& str)又可以将其主要功能交由一个readCompnent函数实现:

    class NewsLetter {
    public:
        NewsLetter(istream& str);
        ...
    private:
        //从str读取下一个NLCompnent的数据,产生组件(compnent),并返回一个指针指向它
        static NLCompnent* readCompnent(istream& str);
        list<NLCompnent*> compnents;
        ...
    };
    NewsLetter::NewsLetter(istream& str){
        while (str) {
           //将readCompnent返回的指针加到compnents list尾端
           compnents.push_back(readCompnent(str));
        }
    }
    View Code

        在这里readCompnent产生一个新的NLCompnent派生对象(或是TextBlock或是Graphic),并返回一个NLCompnent指针,由于它能够产生不同类型对象,因此称它为一个virtual constructor.Virtual constructor在很多情况下都很有用,其中之一就是从磁盘(或网络或磁带)读取对象信息.

        一种特殊的virtual constructor——copy virtual constructor,它返回一个指针指向其调用者的新副本,指针的动态类型由调用它的对象的类型决定.TextBlock和Graphic的copy virtual constructor的可以像这样:

    class NLComponent {
    public:
        // 声明virtual copy constructor
        virtual NLComponent * clone() const = 0;
        ...
    };
    class TextBlock: public NLComponent {
    public:
        virtual TextBlock * clone() const // virtual copy constructor
        { return new TextBlock(*this); }
        ...
    };
    class Graphic: public NLComponent {
    public:
        virtual Graphic * clone() const // virtual copy constructor
        { return new Graphic(*this); } 
        ...
    };
    View Code

        这样就clone就实现了virtual copy constructor的功能.

        注意,虽然derived class重新定义base class的虚函数时,声明必须要与base class中相同,但如果函数返回类型是指向base class的指针(或引用),那么derived class可以返回指向其derived class的指针(或引用).因此以上clone虽然是虚函数,但其返回的指针类型可以不同.

        基于以上NLCompnent的virtual copy constructor,可以为NewsLetter实现(正常的)copy constructor:

    class NewsLetter {
    public:
        NewsLetter(const NewsLetter& rhs);
        ...
    private:
        list<NLComponent*> components;
    };
    NewsLetter::NewsLetter(const NewsLetter& rhs){
        //遍历rhs的list,运用每个元素的virtual copy constructor将元素复制到此对象的compnents list中.
        for (list<NLComponent*>::const_iterator it =rhs.components.begin();it != rhs.components.end();++it)
            //it指向rhs.compnents的目前元素,调用该元素的clone函数取得一个副本并加到本对象的compnents list的尾端
            components.push_back((*it)->clone());
    }
    View Code

    2. virtual non-member function.

        正如constructors无法被虚化,non-member function原则上也无法被虚化——它连成员函数都不是.考虑要为TextBlock和Graphic实现<<操作符,要使<<对TextBlock和Graphic实现不同的行为,直接的思路就是将<<虚化,但实际上这无法实现:<<的第一个操作数是ostream&,也就是说<<要作为member function,就只能成为ostream类的成员,但这是不可能的.因此<<只能为non-member函数,这是可以采取和virtual constructor类似的策略实现virtual non-member function:

    class NLComponent {
    public:
        virtual ostream& print(ostream& s) const = 0;
        ...
    };
    class TextBlock: public NLComponent { bbs.theithome.com
    public:
        virtual ostream& print(ostream& s) const;
        ...
    };
    class Graphic: public NLComponent {
    public:
        virtual ostream& print(ostream& s) const;
        ...
    };
    inline ostream& operator<<(ostream& s, const NLComponent& c){
        return c.print(s);
    }
    View Code

        non-member的虚化和virtual copy constructor的实现类似:写一个虚函数做实际工作,再一个非虚函数调用该虚函数.此外,为了避免函数调用的额外成本(毕竟operator<<只有一句,不值得将压弹栈的成本),可以将非虚函数inlin化.

        到此为止,可以做到让non-member copy constructor视其某个自变量而虚化,要实现根据一个以上自变量的虚化,见条款31.

  • 相关阅读:
    模板笔记2
    模板笔记
    qt打包可执行文件
    合并单独的视频和音频
    模板1
    mysql 数据表中查找重复记录
    mysql左连接右连接(查询两张表不同的数据)
    mysql--构造数据、导入导出
    mysql安装
    linux下启动tomcat服务
  • 原文地址:https://www.cnblogs.com/reasno/p/4841412.html
Copyright © 2020-2023  润新知