构造函数调用虚函数
2015-08-04 14:06
393 查看
今天看android canvas 类的源代码看到
很明显,
在C++中通过构造函数或者析构函数调用虚函数是危险行为,应该尽量避免。
C++中虚函数表会先于构造函数的调用被建立, 当
基本思想就是 把 构造函数中需要调用虚函数的部分分离出来并放到一个
这样我们可以保证 init 被调用的时候
还有一种方法适用于虚函数不需要访问子类的成员的时候,比如我们的
将需要多态支持的部分抽象成一个基类或者接口
继承基类或实现接口
基类构造函数接受一个Helper类的引用:
子类的构造函数传入自定义的Helper
2. https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom.
Canvas的构造函数之一:
public class Canvas{ ... public Canvas() { if (!isHardwareAccelerated()) { ... } else { ... } } public boolean isHardwareAccelerated(){ return false; } } public abstract class HardwareCanvas extends Canvas { @Override public boolean isHardwareAccelerated() { return true; } ... }
很明显,
Canvas构造函数调用的
isHardwareAccelerated()是希望子类重载用的。回想以前写C++代码的时候好像很少碰到在构造函数里面调用虚函数的情形,对于C++如何解决类似的问题似乎有些遗忘了,于是Google了一下,发现这里面还是有一些文章,于是摘抄下来,以备不时之需。
注意:
Calling virtual functions from a constructor or destructor is dangerous and should be avoided whenever possible.在C++中通过构造函数或者析构函数调用虚函数是危险行为,应该尽量避免。
C++现实情形
C++对于构造函数中调用虚函数是支持的,但是需要注意的是当在构造函数中调用虚函数的时候对多态的支持尚未完成。对于上述Canvas中调用
isHardwareAccelerated()的情形, 在C++中是不可能调用到
HardwareCanvas::isHardwareAccelerated()的。
C++中虚函数表会先于构造函数的调用被建立, 当
Canvas调用虚函数
isHardwareAccelerated()的时候, 此时
HardwareCanvas子类的任何信息包括虚表还没有建立起来,因此这时候调用的是
Canvas自己的
isHardwareAccelerated().
Java现实情形
Java采取了与C++的保守相比更激进的做法。在我们创建HardwareCanvas类的实例的时候,它会先把整个类层次结构建立起来,然后再调用基类构造函数和子类的构造函数,这样在
Canvas类的构造函数调用的时候,多态就已经能够发挥作用了。这也是我们能够看到上面的代码的现实原因!
危险在哪里?
可以看到我们的例子中isHardwareAccelerated()仅仅简单的返回了
ture, false, 并没有访问
HardwareCanvas的任何数据。 假如我们的
isHardwareAccelerated()不小心访问了这些数据,那很显然在
HardwareCanvas构造函数没有调用之前,这些数据仅仅是默认值,有可能是 null, 这就给我们隐含了未知的风险,有可能导致运行时的崩溃。
C++的解决方法
C++中的解决方法其实很简单,简单到我们大多数人一直在用却不知道原因。class Canvas { public: typedef std::unique_ptr<Canvas> Ptr; void init() { ... if(!isHardwareAccelerated() ) { ... } else { ... } } protected: virtual bool isHardwareAccelerated(){ return false;} public: static Ptr create() { Ptr p( /*...use a factory to create a HardwareCanvas object via new...*/ ); p->init(); return p; } protected: Canvas(); }; class HardwareAccelerated : public Canvas { protected: bool isHardwareAccelerated() { return true; } };
基本思想就是 把 构造函数中需要调用虚函数的部分分离出来并放到一个
init函数中。
这样我们可以保证 init 被调用的时候
HardwareCanvas构造函数已经被创建, 此时访问它的成员就安全了。
还有一种方法适用于虚函数不需要访问子类的成员的时候,比如我们的
Canvas类的
isHardwareAccelerated().
将需要多态支持的部分抽象成一个基类或者接口
class Helper { public: virtual bool isHardwareAccelerated(){ return false;} };
继承基类或实现接口
class Helper_A : public Helper { public: virtual bool isHardwareAccelerated() { return true;} };
基类构造函数接受一个Helper类的引用:
class Canvas { public: Canvas( Helper& ); };
子类的构造函数传入自定义的Helper
class HardwareCanvas : public Canvas { public: HardwareCanvas( ) : Canvas( getHelper()){} private: static Helper_A& getHelper(); };
参考:
1. http://stackoverflow.com/questions/962132/calling-virtual-functions-inside-constructors.2. https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom.
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- C++ Custom Control控件向父窗体发送对应的消息
- C++中拷贝构造函数的应用详解