0

在阅读 Essential c++ 第 5.10 章运行时类型识别时,我遇到了一个问题。我先介绍一下背景。有一个名为的基类num_sequence和一个Fibonacci派生自的类num_sequence。在基类中,有一个名为的虚函数gen_elems,派生类有自己的定义。

以下内容来自书中。

Fibonacci fib;
num_sequence *ps = &fib;
ps->gen_elems(64);

我们知道 gen_elems() 的斐波那契实例将被调用。然而,虽然我们从这个测试中知道 ps 寻址一个斐波那契类对象,但尝试直接通过 ps 调用 gen_elems() 的斐波那契实例会导致编译时错误:

ps->斐波那契::gen_elems(64); //给出编译时错误

ps 不知道它所寻址的对象的类型,即使我们和 typeid 和虚函数机制知道。

要调用 gen_elems() 的斐波那契实例,我们必须指示编译器将其转换ps为斐波那契类型的指针。static_cast 和 dynamic_cast 都可以完成这项工作。

我被粗体字弄糊涂了。ps->gen_elems(64)实际上调用 的 Fibonacci 实例gen_elems()。为什么我们需要使用 static_cast 和 dynamic_cast 将其转换为斐波那契类型的指针?

4

1 回答 1

4

大多数情况下,您只需正常调用虚方法,并让多态根据需要调用最派生的实现。作者试图解释的是,也可以不经过多态直接调用虚方法的具体实现。

假设一个类派生自再次Fibonacci覆盖gen_elems(),但您不想调用该覆盖,您想调用Fibonacci覆盖。通过在编译时将num_sequence指针转换为Fibonacci指针(或后代指针),它允许编译器访问Fibonccivtable,因此它可以发出代码以直接调用Fibonacci::gen_elems()(如果在运行时指向的对象实际上不是Fibanocci或后代,您可能会崩溃/损坏您的应用程序。这无法在编译时验证)。

例如:

class num_sequence
{
public:
    virtual void gen_elems(int value)
    {
        std::cout << "num_sequence" << std::endl;
    }
};

class Fibonacci : public num_sequence
{
public:
  void gen_elems(int value)
  {
        std::cout << "Fibonacci" << std::endl;
  }
};

class SomethingElse : public Fibonacci
{
public:
  void gen_elems(int value)
  {
        std::cout << "SomethingElse" << std::endl;
  }
};

.

num_sequence ns;
Fibonacci fib;
SomethingElse se;
num_sequence *ps;

ps = &ns;
ps->gen_elems(64); // displays "num_sequence"

ps = &fib;
ps->gen_elems(64); // displays "Fibonacci"

ps = &se;
ps->gen_elems(64); // displays "SomethingElse"

ps->Fiboacci::gen_elems(64); // compiler error!

static_cast<Fibonacci*>(ps)->Fibonacci::gen_elems(64); // displays "Fibonacci"
static_cast<SomethingElse*>(ps)->Fibonacci::gen_elems(64); // displays "Fibonacci"

Fibonacci *pfib = dynamic_cast<Fibonacci*>(ps);
if (pfib != NULL)
{
    pfib->gen_elems(64); // displays "SomethingElse"
    pfib->Fibonacci::gen_elems(64); // displays "Fibonacci"
}
于 2013-02-09T03:44:30.737 回答