虚拟函数的静态决议 和 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。

原文地址:https://www.cnblogs.com/ziyoudefeng/p/2407907.html