Effective C++——条款9(第2章)

条款09:    绝不在构造和析构过程中调用 virtual 函数

Never call virtual functions during construction or destruction

不应该在构造函数和析构函数期间调用 virtual 函数,因为这样的调用不会带来预想的结果.

假设有个 class 继承体系,用来模塑股市交易如买进,卖出的订单等等.这样的交易一定要经过审计,所以每当创建一个交易对象,在审计日志中也需要创建一笔适当记录.下面是一个看起来颇为合理的做法:

class Transaction {
public:
    Transaction();
    virtual void logTransaction() const = 0;
    ...
};
Transaction::Transaction() {
    logTransaction();
}
class BuyTransaction : public Transaction {
public:
    virtual void logTransaction() const;
};
class SellTransaction : public Transaction {
public:
    virtual void logTransaction() const;
};

现在,当以下这行被执行,会发生什么事:

BuyTransaction b;

无疑地有一个BuyTransaction构造函数被调用,但首先Transaction构造函数一定会更早地被调用.derived class 对象内的base class 成分会在derived class 自身成分在构造之前先构造,Transaction构造函数的最后一行调用 virtual 函数logTransaction,这正是引发惊奇的起点.这时候被调用的logTransaction是Transaction内的版本,不是BuyTransaction内的版本,即使目前即将建立的对象类型是BuyTransaction.base
class 构造期间 virtual 函数绝不会下降到derived class 阶层.非正式的说话比较传神:在base class 构造期间,virtual 函数不是 virtual 函数.

根本的原因是:在derived class 对象的base class 构造期间,对象的类型是base class 而不是derived class.不只是 virtual 函数会被编译器解析至base class.若使用运行期类型信息(如 dynamic_cast 和 typeid)也会把对象视为base class 类型。对象在derived class 构造函数开始执行前不会成为一个derived
class 对象.

相同道理也适用于析构函数.一旦derived class 析构函数开始执行,对象内derived class 成员变量便开始呈现未定义值,所以C++视它们仿佛不存在.进入base class 析构函数后对象就成为一个base class 对象,而C++的任何部分包括 virtual 函数,dynamic_cast 等等也那么看待它.

一个好的做法就是:确定构造函数和析构函数都没有(在对象被创建和被销毁期间)调用 virtual 函数,而它们调用的所有函数都服从同一约束.

 注意:

在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 11:40:14

Effective C++——条款9(第2章)的相关文章

Effective C++——条款13(第3章)

第3章    资源管理 Resource Management 所谓资源就是,一旦用了它,将来必须还给系统.C++程序中最常使用的资源就是动态内存分配(如果分配内存从来都增归还,会导致内存泄露).其他常见的资源还有文件描述符(file descriptors),互斥锁(mutex locks),图形界面中的字型和笔刷,数据库连接,以及网络sockets.不论哪一种资源,重要的是,不再使用它时,必须将它还给系统. 条款13:    以对象管理资源 Use objects to manage res

Effective C++——条款5(第2章)

第2章    构造/析构/赋值运算 Constructors,Destructors,and Assignment Operator 几乎每一个 class 都会有一个或多个构造函数,一个析构函数,一个copy assignment 操作符. 条款05:    了解C++默默编写并调用哪些函数 Know what functions C++ silently writes and calls 什么时候empty class 不再是个empty class 呢?当C++处理过它之后.如果自己没有声

Effective C++——条款15(第3章)

条款15:    在资源管理类中提供对原始资源的访问 Provide access to raw resources in resources-managing classes 资源管理类(resource-managing classes)很棒.它们是对抗资源泄露的堡垒.在一个良好的环境中将依赖这样的classes来处理和资源之间的所有互动.而不是直接处理原始资源,但这个环境并不完美,许多API直接涉及资源,因此有时只能绕过资源管理对象直接访问原始资源(raw resources). 例如,条

Effective C++——条款14(第3章)

条款14:    在资源管理类中小心copying行为 Think carefully about copying behavior in resource-managing classes 条款13导入这样的观念:"资源取得时机便是初始化时机"(Resource Acquisition Is Initializaiton,RAII),并以此作为"资源管理类"的脊柱,也描述了auto_ptr和tr1::shared_ptr如何将这个观念表现在heap-based资源

Effective C++——条款7(第2章)

条款07:    为多态基类声明 virtual 析构函数 Declare destructors virtual in polymorphic base classes 设计以下时间基类TimeKeeper: class TimeKeeper { public: TimeKeeper(); ~TimeKeeper(); }; class AtomicClock : public TimeKeeper { ... }; class WaterClock : public TimeKeeper {

Effective C++——条款6(第2章)

条款06:    若不想使用编译器自动生成的函数,就该明确拒绝 Explicitly disallow the use of compiler-generated functions you do not want. 在某些情况下,希望保持对象的唯一性,不想让对象有其他副本.如下: class HomeForSale { ... }; HomeForSale h1; HomeForSale h2; HomeForSale h3(h1); // 企图拷贝h1,不应该通过编译 h1 = h2; //

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的特性

effective c++ 条款4 make sure that objects are initialized before they are used

1 c++ 类的数据成员的初始化发生在构造函数前 class InitialData { public: int data1; int data2; InitialData(int a, int b) { data1 = a: //this is assignment data2 = b; //this is assignment } /* InitialData(int a, int b):data1(a),data2(b) //this is initial {} */ } 2 不同cpp文

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数