• MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化


    1.virtual constructor

    在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所涉及的

    virtual-constructor实际上是"仿virtual-constructor.

    假设你设计一个软件,用来处理新闻事件,它由文字和图形构成

    class NLComponent { //用于 newsletter components
    public: // 的抽象基类
    ... //包含至少一个纯虚函数
    };
    class TextBlock: public NLComponent {
    public:
    ... // 不包含纯虚函数
    };
    class Graphic: public NLComponent {
    public:
    ... // 不包含纯虚函数
    };
    class NewsLetter { // 一个 newsletter 对象
    public: // 由 NLComponent 对象
    ... // 的链表组成
    private:
    list<NLComponent*> components;
    };

    这些class的彼此关系如下图

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

    class NewsLetter 
    {
    public:
      NewsLetter(istream& str);
      ...
    };
    //此构造函数的伪代码是这样的:
    NewsLetter::NewsLetter(istream& str)
    {
      while (str) 
      {
        //从 str 读取下一个 component 对象;
        //把对象加入到 newsletter 的 components 对象的链表中去;
      }
    }

    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));
        }
    }

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


    2.virtual copy 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); } 
        ...
    };

    这样就clone其实就是实现virtual copy constructor

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

    下面我们实现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());
    }

    3.Non-Member Functions 的行为虚化

    正如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);
    }

    小结:

    构造函数的虚化: 可以使用使用拷贝构造函数,通过返回不同的派生类的指针来模拟出构造不同的对象.

    没有成员变量的函数的虚化: 用一个虚函数来封装想要虚化的函数.

  • 相关阅读:
    Vue路由机制
    谷歌浏览器打不开应用商店的解决方法
    Vue报错——Component template should contain exactly one root element. If you are using vif on multiple elements, use velseif to chain them instead.
    Vue.js学习之——安装
    Vue使用axios无法读取data的解决办法
    关于localstorage存储JSON对象的问题
    2013年整体计划
    个人喜欢的警语收集
    Linux防火墙的关闭和开启
    Flex修改title 转载
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/6938098.html
Copyright © 2020-2023  润新知