虚拟函数的静态决议 和 RTTI 小例子
一:先说虚拟函数的静态决议(Static Resolution)
在两种情况下,虚拟函数机制不会出现预期行为:1、在基类的constructor和destructor内;2、当我们使用的是基类的对象,而非基类对象的pointer 或 reference时。
上述第二种情况很好理解,第二种情况是C++多态机制的重要概念。第一种情况其实也很简单,但我才刚开始学习C++,怕以后自己会不小心把一些虚函数写在constructor 或 destructor里,固写此文章记录一下,下面给出第一种情况的解释:
-----------------摘抄Essential C++解释开始-----------------
当我们构造派生类对象时,基类的constructor会先被调用。如果在基类的constructor中调用某个虚拟函数,会发生什么事?调用的应该是派生类所定义的那一份吗?
问题出在此刻派生类中的data member尚未初始化。如果此时调用派生类的那一份虚拟函数,它便有可能取用未经初始化的data members,这可不是一件好事。
基于这个理由,在基类的constructor中,派生类的虚拟函数绝对不会被调用。同理,如果在基类的destructor中调用虚拟函数,此规则同样成立。
-----------------摘抄Essential C++解释结束-----------------
举例:
基类代码
1 #pragma once
2 #include <typeinfo>
3 #include <iostream>
4 class num_sequence
5 {
6 public:
7 num_sequence(void);
8 virtual const char* what_am_i() const;
9 virtual void display() const { std::cout << "Based decontroctor" << std::endl; }
10 virtual ~num_sequence(void) { display(); }
11 };
派生类代码
1 #pragma once
2 #include "num_sequence.h"
3
4 class Fibonacci :
5 public num_sequence
6 {
7 public:
8 Fibonacci(void);
9 virtual void display() const { std::cout << "Drived destructor" << std::endl; }
10 ~Fibonacci(void) { display(); };
11 void testTypeId() { std::cout << "testTypeIdFunction" << std::endl; }
12 };
然后在main里测试:
Fibonacci fib;
num_sequence *ps = &fib;
输出的结果是:
Derived destructor
Based destructor
请按任意键继续. . .
即:分别调用子类和基类的析构函数(调用基类的析构函数时,基类析构函数里的display()函数没有动态执行子类的display())
二:RTTI
基类num_sequence.cpp中加入
inline const char* num_sequence::what_am_i() const
{
return typeid( *this ).name();
}
主main程序里面:
1 Fibonacci fib;
2 num_sequence *ps = &fib;
3
4 if ( typeid(*ps) == typeid(Fibonacci) )
5 {
6 ps->Fibonacci::testTypeId(); // 错误
7 ps->testTypeId();// 错误
8 if ( Fibonacci *pf = dynamic_cast<Fibonacci*> (ps) )
9 {
10 pf->testTypeId();
11 }
12 }
Line6、Line7错误,这里ps并不"知道"它所寻址的对象实际上是什么型别--纵使我们知道,typeid及虚拟函数机制也知道。。
为了调用Fibonacci所定义的testTypeId(),我们必须指示编译器,将ps的型别转换为Fibonacci指针。
1、static_cast可以转换:Fibonacci *pf = static_cast<Fibonacci*>( ps );
但存在危险,因为编译器无法确认我们所进行的转换操作是否完全正确。
2、dynamic_cast:这是一个RTTI运算法,会进行执行期检验操作,检验ps所指对象是否属于Fibonacci类。如果是,转换操作便会发生,于是pf便指向该Fibonacci对象。如果不是,dynamic_cast运算符返回0。