Effective C++(9) 构造函数调用virtual函数会发生什么
2013-12-26 15:34
369 查看
问题聚焦:
让我先来看一下在构造函数里调用一个virtual函数会发生什么结果
Demo
执行上述代码,会发现链接出错,报错:
表示没有Transaction::logTransaction() const的实现。
现在把上述代码中被注释的代码补上之后,再编译一次,OK,通过了。来看看结果是什么吧
不错,运行正常,也没有crash。不过,貌似不是我们想要的结果。
我们是对BuyTransaction类进行实例化,但是构造函数调用的是父类的构造函数,这是为什么呢?
下面我们来分析一下原因:
父类构造函数先调用,此时子类的局部成员变量还没准备好,这时如果将virtual函数下降至子类阶层,那么使用未初始化的部分可能会引起不明确的行为,所以C++直接禁止了这种行为,因此会出现上面的链接错误。
在子类的父类部分被构造期间,virtual函数并不认为是virtual函数,因为在初始化子类的父类部分时,编译器认为当前的对象是父类型,同时,直接认为子类部分是不存在的,因为子类成分并没有被初始化。
问题很明显了,就是在构造函数和析构函数中调用了virtual函数导致了上面的问题。那么有什么解决方案呢?
解决方案:令子类将必要的构造信息向上传递至base class构造函数
Demo
打印结果:
这是我们想要的结果。
小结:
在构造和析构函数内不要调用virtual函数,因为这类调用不下降至子类。
参考资料:
《Effective C++ 3rd》
不要在构造函数和析构函数中调用virtual函数,因为这样的调用不会带来你预想的结果。
让我先来看一下在构造函数里调用一个virtual函数会发生什么结果
Demo
class Transaction { public: Transaction(); virtual void logTransaction() const = 0; }; Transaction::Transaction() { logTransaction(); } //void Transaction::logTransaction() const //第一次编译时,注释掉该段代码 //{ // std::cout << "Transaction called" << std::endl; //} class BuyTransaction: public Transaction { public: virtual void logTransaction() const; }; void BuyTransaction::logTransaction() const { std::cout << "BuyTransaction called" << std::endl; } //执行下面的语句会发生什么事情? BuyTransaction b;
执行上述代码,会发现链接出错,报错:
表示没有Transaction::logTransaction() const的实现。
现在把上述代码中被注释的代码补上之后,再编译一次,OK,通过了。来看看结果是什么吧
不错,运行正常,也没有crash。不过,貌似不是我们想要的结果。
我们是对BuyTransaction类进行实例化,但是构造函数调用的是父类的构造函数,这是为什么呢?
下面我们来分析一下原因:
父类构造函数先调用,此时子类的局部成员变量还没准备好,这时如果将virtual函数下降至子类阶层,那么使用未初始化的部分可能会引起不明确的行为,所以C++直接禁止了这种行为,因此会出现上面的链接错误。
在子类的父类部分被构造期间,virtual函数并不认为是virtual函数,因为在初始化子类的父类部分时,编译器认为当前的对象是父类型,同时,直接认为子类部分是不存在的,因为子类成分并没有被初始化。
问题很明显了,就是在构造函数和析构函数中调用了virtual函数导致了上面的问题。那么有什么解决方案呢?
解决方案:令子类将必要的构造信息向上传递至base class构造函数
Demo
/** main.h **/ #include<iostream> #include<string> class Transaction { public: explicit Transaction(const std::string& logInfo); void logTransaction(const std::string& logInfo) const; // 不再是虚函数 }; Transaction::Transaction(const std::string& logInfo) { logTransaction(logInfo); } void Transaction::logTransaction(const std::string& logInfo) const { std::string s = logInfo; std::cout << s << std::endl; } class BuyTransaction: public Transaction { public: BuyTransaction(const std::string& logInfo); }; BuyTransaction::BuyTransaction(const std::string& logInfo):Transaction(logInfo) // 把信息传递给父类,通过父类打印出log { std::cout << "BuyTransaction called" << std::endl; } /** main.cpp **/ #include<iostream> #include"main.h" int main() { BuyTransaction b("hello world"); system("Pause"); return 0; }
打印结果:
这是我们想要的结果。
小结:
在构造和析构函数内不要调用virtual函数,因为这类调用不下降至子类。
参考资料:
《Effective C++ 3rd》
相关文章推荐
- Effective C++(9) 构造函数调用virtual函数会发生什么
- Effective C++ Item 09-绝不在构造函数和析构函数中调用virtual函数
- 使用new操作符来调用一个构造函数的时候发生了什么
- 9 绝对不要在构造函数和析构函数中调用virtual函数
- 调用malloc时发生了什么(2) - sys_brk函数与VMA
- 读书笔记_Effective_C++_条款九:绝不在构造和析构函数中调用virtual函数
- 当调用标准的malloc函数时,内核发生了什么
- effective C++ 条款 9:绝不在析构和构造函数中调用virtual函数
- 函数调用时发生了什么
- Effective C++ Item 9 绝不在构造和析构过程中调用virtual函数
- Effective C++: 尽量不要在构造函数中调用虚函数(virtual-function).
- [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同
- Effective C++ Item 9 绝不在构造和析构过程中调用virtual函数
- 调用malloc()函数之后,内核发生了什么?附malloc()和free()实现的源代码
- 调用malloc时发生了什么(1) - brk与sbrk
- 函数调用时,栈里到底发生了什么?
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 错例:做类扩展时不调用父类方法 会发生什么?
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
- C++13.1.2合成复制构造函数----定义复制对象时会发生什么