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

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

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在很多情况下都很有用,其中之一就是从磁盘(或网络或磁带)读取对象信息.

一种特殊的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虽然是虚函数,但其返回的指针类型可以不同.

基于以上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());
}

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

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

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

时间: 2024-10-19 17:16:25

More Effective C++ 条款25 将constructor和non-member function虚化的相关文章

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

1.virtual constructor 在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所涉及的 virtual-constructor实际上是"仿virtual-constructor. 假设你设计一个软件,用来处理新闻事件,它由文字和图形构成 class NLComponent { //用于 newsletter components public: // 的抽象基类 ... //包

Effective C++ 条款25 考虑写出一个不抛出异常的swap函数

1. swap是STL的一部分,后来成为异常安全性编程(exception-safe programming)(见条款29)的一个重要脊柱,标准库的swap函数模板定义类似以下: namespace std{ template<typename T> swap(T& lhs,T& rhs){ T temp(lhs); lhs=rhs; rhs=temp; } } 只要T类型支持拷贝构造以及拷贝赋值,标准库swap函数就会调用T的拷贝构造函数和拷贝构造操作符完成值的转换,但对于某

effective C++ 条款25 swap

item 25:一个不抛异常的swap函数 标准库有一个swap用于交换两个对象值 namespace std{ template<typename T> void swap(T& a , T& b) { T temp(a) ; a = b ; b = temp ; } } 所以,只要对象的类型T支持copying(copy ctor 和 copy assignment),那么你就可以用这个标准库的swap,但是,你必须保证,你swap的时候,上述操作是你需要的,有的时候,没有

Effective C++ 条款25

考虑写出一个不抛出异常的swap函数 本节讲解如何自定义一个高效的swap函数 对于std名空间中的swap缺省函数如下所示 namespace std{ template<typename T> void swap(T& a, T& b) { T temp(a); a=b; b=temp; } } class WidgetImpl{ public: -- private: int a,b,c; //数据很多,复制意味时间很长 std::vector<double>

Effective C++ -----条款25:考虑写出一个不抛异常的swap函数

当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于class(而非templates),也请特化std::swap. 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”. 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西.

effective c++ 条款9 do not call virtual function in constructor or deconstructor

在构造函数中不要调用virtual函数,调用了也不会有预期的效果. 举个例子 class Transaction { public: Transaction() { log(); } virtual void log() =0; } class BusinessTransaction: public Transaction { public: virtual void log() { ;//log something here } } BusinessTransaction b_trx; b_t

More Effective C++----技巧 &amp; (25)将构造函数和非成员函数虚拟化

技巧 本书涉及的大多数内容都是编程的指导准则.这些准则虽是重要的,但是程序员不能单靠准则生活.有一个很早以前的卡通片叫做"菲利猫"(Felix the Cat), 菲利猫无论何时遇到困难,它都会拿它的trick包.如果一个卡通角色都有一个trick包,那么C++程序员就更应该有了.把这一章想成你的trick包的启动器. 当设计C++软件时,总会再三地受到一些问题的困扰.你如何让构造函数和非成员函数具有虚拟函数的特点?你如何限制一个类的实例的数量?你如何防止在堆中建立对象呢?你如何又能确

More Effective C++ 条款35 让自己习惯于标准C++ 语言

(由于本书出版于1996年,因此当时的新特性现在来说可能已经习以为常,但现在重新了解反而会起到了解C++变迁的作用) 1. 1990年后C++的重要改变 1). 增加了新的语言特性:RTTI,namespaces,bool,关键词mutable和explicit,enums作为重载函数之自变量所引发的类型晋升转换,以及"在class 定义区内直接为整数型(intergral) const static class members设定初值"的能力. 2). 扩充了Templates的特性

More Effective C++ 条款28 Smart Pointers(智能指针)

1. 智能指针(如标准库的auto_ptr,shared_ptr,weak_ptr,boost的scoped_ptr等)主要用于动态内存的管理,同时提供给用户与内置指针一样的使用方法,本条款主要涉及智能指针在构造与析构,复制和赋值,解引等方面的注意点,而非智能指针的实现细节. 2. 智能指针的构造,赋值,析构 智能指针的copy constructor,assignment operator,destructor对应于不同的观念而有不同的实现,主要有三种选择: 1).不允许对象的共享,在调用co