zoukankan      html  css  js  c++  java
  • 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);
    }

    小结:

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

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

  • 相关阅读:
    canvas---HTML5新特性
    flex布局之兼容
    前端Blob对二进制流数据的处理方式
    execCommand的复制
    express快速入门
    react+redux+webpack+git技术栈
    react开发
    gulp工程化工具
    python---django安装
    vue+webpack+element-ui+git
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/6938098.html
Copyright © 2011-2022 走看看