Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数
(整理自Effctive C++,转载请注明。整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/)
为方便采用书上的例子,先提出问题,在说解决方案。
1 问题
1: class Transaction{
2: public:
3: Transaction();
4: virtual void LogTransaction() const = 0 ;
5: ...
6: };
7:
8: Transaction::Transaction() //Base class 的构造函数之实现
9: {
10: ...
11: LogTransaction(); //最后动作是调用LogTransaction函数
12: }
13:
14: class BuyTransaction:public Transaction{
15: public:
16: virtual void LogTransaction() const ;
17:
18: };
假设在程序中:
1: BuyTransaction b ;
BuyTransaction构造函数会被调用,但首先Transaction的构造函数会被调用;derived class对象内的base class成分会在derived class自身成分被构造之前先构造妥当。Transaction构造函数最后一行调用virtual函数LogTransaction是base class内的版本。是的,base class构造期间virtual函数绝对不会下降到derived class阶层。即“在base class 构造期间,virtual函数不是virtual函数”。
这一似乎反直觉的行为有个好理由:对于base class构造函数的执行更早于derived class构造函数,当base class构造函数执行时derived class的成员变量尚未初始化。如果此期间调用virtual函数下降至derived class阶层,要知道derived class的函数几乎必然取用local成员变量,而那些变量尚未初始化,C++不允许你这样做。
其实还有更根本的原因:在derived class对象的base class构造期间,对象的类型是derived class。
相同道理也适用于析构函数。一旦derived class析构函数开始执行,对象内的derived class 成员变量便呈现未定义的值。进入base class析构函数后对象就成为一个base class对象,而virtual函数等也那么看待它。
另外,侦测“构造函数或析构函数运行期间是否调用virtual函数”并不是很轻松。唯一能够避免此问题的做法就是:确定你的构造函数和析构函数都没有(在对象被创建和被销毁期间)调用virtual函数,而它们调用的所有函数也都服从同一约束。
2 解决方案
上面提到唯一能够避免此问题的做法就是:确定你的构造函数和析构函数都没有(在对象被创建和被销毁期间)调用virtual函数,而它们调用的所有函数也都服从同一约束。
但是,如何确保每次一继承体系上的对象被创建,都有适当版本的函数被调用呢?很显然,在base class构造函数内对着对象调用virtual函数是一种错误的做法。其他方案可以解决这个问题:一种做法是在class Transaction内将LogTransaction函数改为non-virtual,然后derived class构造函数传递必要的信息给Transaction构造函数,然后那个构造函数便可安全调用non-virtual LogTransaction:
1: class Transaction{
2: public:
3: explicit Transaction ( const string& logInfo ) ;
4: void LogTransaction ( const string& logInfo ) const ;//如今是个non-virtual函数
5: ...
6: };
7: Transaction::Transaction( const string& logInfo )
8: {
9: LogTransaction(logInfo); //如今是个non-virtual 调用
10: }
11:
12: class BuyTransaction:public Transaction{
13: public:
14: BuyTransaction( parameters ):Transaction(createLogString(parameters)) //将log信息传递给base class构造函数
15: {...}
16: ...
17: private:
18: static string createLogString(parameters) ;
19: };
20:
换句话说由于无法使用virtual函数从base class向下调用,在构造函数期间,你可以借由“令derived class将必要的构造信息传递至base class构造函数”替换之而加以补偿。
令createLogString为static,也就不可能意外指向“初期未成熟之BuyTransaction对象尚未初始化的成员变量”。这很重要,正因为“那些成员变量处于未定义状态”,所以在“base class构造和析构期间调用的virtual函数不可下降至derived class” 。
请注意:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class。
Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数的更多相关文章
- Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).
- Effective C++ 条款九、十 绝不在构造和析构过程中调用virtual函数|令operator=返回一个reference to *this
1.当在一个子类当中调用构造函数,其父类构造函数肯定先被调用.如果此时父类构造函数中有一个virtual函数,子类当中也有,肯定执行父类当中的virtual函数,而此时子类当中的成员变量并未被初始 ...
- 条款9:绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruction)
NOTE:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)
- 条款09:绝不在构造和析构过程中调用virtual函数
不该在构造函数和析构函数期间调用virtual函数,这一点是C++与jave/C#不同的地方之一. 假设有一个class继承体系,用来模拟股市交易如买进.卖出的订单等等.这样的交易一定要经过审计,所以 ...
- NO.8:绝不在构造或者析构过程中调用virtual函数
在构造和析构执行期间不要调用virtual函数,因为这类调用从不会下降至derived class(比起当前执行构造函数和析构函数) 如果在base class 构造函数或者析构函数调用virtual ...
- 【09】绝不在构造和析构过程中调用virtual方法
1.绝不在构造和析构过程中调用virtual方法,为啥? 原因很简单,对于前者,这种情况下,子类专有成分还没有构造,对于后者,子类专有成分已经销毁,因此调用的并不是子类重写的方法,这不是程序员所期望的 ...
- 条款9:不要在构造和析构过程中调用virtual函数
如下是一个股票交易的例子: class Transaction // 交易的基类 { public: Transaction(); ; // 用于记录交易日志 }; Transaction::Tran ...
- Effective C++ .09 不在构造和析构过程中调用virtual函数
看过C++对象模型的话就可以知道,在构造基类时,完整的vtable没有建立起来(表项没有被相应的子类函数替换),因而无法调用到子类的函数(即构造函数中的virtual函数是本类里的方法,不是virtu ...
- 绝不要在构造函数和析构过程中调用virtual函数
下面是一个用来塑模股市交易的类: derived的类的构造函数被调用,但是首先得调用基类Transaction的构造函数,但是在后面还得调用virrual函数,这个时候子类的对象的构造还没有完成,那么 ...
随机推荐
- CGroup 介绍、应用实例及原理描述
CGroup 介绍 CGroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制.记录.隔离进程组 (process groups) 所使用的物力资源 (如 cpu ...
- Codeforces Round #254 (Div. 2) DZY Loves Chemistry【并查集基础】
一开始不知道题意是啥意思,迟放进去反应和后放进去反应有什么区别 对于第三组数据不是很懂,为啥312,132的组合是不行的 后来发现这是一道考察并查集的题目 QAQ 怒贴代码: #include < ...
- X-UA-Compatible是什么
X-UA-Compatible是神马? X-UA-Compatible是IE8的一个专有<meta>属性,它告诉IE8采用何种IE版本去渲染网页,在html的<head>标签中 ...
- SUP (SAP Mobile SDK 2.2) 连接 Sybase SQL Anywhere sample 数据库
安装了 SAP Mobile SDK 2.2 后发现,这个版本没有自带Sybase SQL Anywhere 数据库. 解决办法: 1. 免费下载 SQL Anywhere Develope ...
- Cannot drop the database ‘XXX’ because it is being used for replication.
删除订阅数据库的时候出现下面的错误: Cannot drop the database ‘XXX’ because it is being used for replication. 数据库的状态为 ...
- datanode启动后,在web50070port发现不到datanode节点(能力工场)
直接上问题:这两天为了试验,安装了两套集群: (1)32位hadoop1集群(5个节点); (2)64位hadoop2集群(6个节点) 两个集群中都遇到过这种问题:在namenode正常启动hadoo ...
- sql基础,必须会的————随便整理、杂乱无章
1.sqlserver2008r2的安装 2.数据库与表的建立.增加.删除.修改. 3,索引的概念,包括聚集与非聚集的区别.全文索引的建立与如何使用全文索引. 4,重新生成索引,重新组织索引. 5,建 ...
- MySQL新建用户,授权,删除用户,修改密码等命令
首先要声明一下:一般情况下,修改MySQL密码,授权,是需要有mysql里的root权限的. 注:本操作是在WIN命令提示符下,phpMyAdmin同样适用. 用户:phplamp 用户数 ...
- Win7 和 MAC 系统通过VMware共享文件夹(简单又好用,几乎什么都不用设置)
Win7是Server,Mac是Client,VMware上运行Mac系统 1.在VMware的Options菜单中选择Shared Folders选项 2.选择Always enabled选项 3. ...
- java jni 编程
最近要学习Java JNI 编程. 我使用的是的windows系统.装了一个cygwin. 根据 <JNI 编程规范和指南>. 文件网址: http://wenku.baidu.com/v ...