如何通过基类指针调用派生类的虚函数

6

让我们看一下这段代码:

class CBase
{
 public:
    virtual vfunc() { cout << "CBase::vfunc()" << endl; }
};

class CChild: public CBase
{
 public:
    vfunc() { cout << "CChild::vfunc()" << endl; }
};

int main() 
{
 CBase *pBase = new CBase;
 ((CChild*)pBase)->vfunc(); // !!! important 
 delete pBase;
 return 0;
}

输出结果为:
CBase::vfunc()

但我想看到:CChild::vfunc()

使用((CChild*)pBase)显式转换为类型"CChild*"。那么为什么要调用派生的vfunc()函数,我需要将"important"字符串替换为:((CChild*)pBase)->CChild::vfunc();

4个回答

6
这不是它的工作原理 - 这才是:
CBase *pBase = new CChild;
pBase->vfunc();

虚函数调用是通过指针和引用动态解析的(除非您显式地调用该方法,就像您所做的那样)。这意味着无论您告诉编译器指针是什么,它都会在vftable中查找方法。在您的情况下,这是CBasevftable


5

你无法这样做。 *pBase 是一个类型为CBase的对象。你不能把它当作CChild来使用,因为它不是一个CChild对象。

使用强制转换得到的CChild*指针会导致程序表现出未定义的行为。


1
那么如何理解Schildt H.所说的:“基类指针也可以用作指向任何派生自该基类的类对象的指针”? - LEXX
那么为什么字符串 ((CChild*)pBase)->CChild::vfunc() 能够工作呢? - LEXX
@LEXX:首先,你应该烧掉那本书,然后去找一本好的入门书。虽然他在那个引用中说的是正确的。一个基类指针(CBase*)可以指向从CBase派生出来的类型的对象(比如CChild)。但这不是你所做的:你的基类指针指向了一个基类对象。如果你有一个例子,比如CBase* p = new CChild();,那么p将是一个指向派生类对象的基类指针。 - James McNellis
1
@LEXX:这个表达式不起作用。它看起来“工作”了,但它很可能会导致你的程序崩溃,或者导致你的计算机爆炸。你的程序表现出未定义行为,这意味着它可以做任何事情。 - James McNellis

2
其他答案提出了重要的观点-补充:如果您实际上正在处理一个CChild(例如,它是作为参数传递的引用),那么您可以使用dynamic_cast进行向下转换。然而,过度依赖dynamic_cast通常表明您的设计出现了问题。
有关强制转换的详细信息,请参见: http://msdn.microsoft.com/en-us/library/cby9kycs(v=vs.80).aspx 因此,该过程将涉及通过dynamic_castCBase参数转换为CChild,如果引用是CChild并且dynamic_cast成功,则可以确定您正在处理一个CChild,然后可以安全地将其用作CChild

1
这里的问题似乎很简单。CBase 无法神奇地升级为 CChild!让我重新编写您的示例并添加一些注释。应该是不言自明的...
#include <iostream>

class CBase {
public:
    virtual void vfunc() { std::cout << "CBase::vfunc()" << std::endl; }
    virtual ~CBase(){} // Virtual destructor... extremely important! I'll let you figure out why as an excercise
};

class CChild: public CBase {
public:
    void vfunc() { std::cout << "CChild::vfunc()" << std::endl; }
    ~CChild(){} // Is this destructor called? When? Try adding some std::cout to each destructor
};

int main() 
{
    CBase *ptr1 = new CBase;
    CBase *ptr2 = new CChild;

    ptr1->vfunc(); // ptr1 points to an instance of CBase. This is what's important!!
    ptr2->vfunc(); // ptr2 points to an instance of CChild, which can be referenced to as a CBase

    delete ptr1;
    delete ptr2;
}

输出:

CBase::vfunc()
CChild::vfunc()

PS:我刚意识到自己晚了大约5年才来参加这个派对,但由于我认为这很有教育价值,所以我还是要发布它!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接