以下是来自 http://www.cplusplus.com/doc的多态性示例/tutorial/polymorphism.html (为便于阅读而编辑):

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
    protected:
        int width;
        int height;
    public:
        void set_values(int a, int b) { width = a; height = b; }
        virtual int area(void) =0;
};

class Rectangle: public Polygon {
    public:
        int area(void) { return width * height; }
};

class Triangle: public Polygon {
    public:
        int area(void) { return width * height / 2; }
};

int main () {
    Rectangle rect;
    Triangle trgl;
    Polygon * ppoly1 = &rect;
    Polygon * ppoly2 = &trgl;
    ppoly1->set_values (4,5);
    ppoly2->set_values (4,5);
    cout << ppoly1->area() << endl; // outputs 20
    cout << ppoly2->area() << endl; // outputs 10
    return 0;
}

我的问题是编译器如何知道ppoly1是一个Rectangle并且ppoly2是一个三角形,以便它可以调用正确的area()函数?它可以通过查看“Polygon * ppoly1 =&#9645;”来找到它。并且知道rect是一个Rectangle但是在所有情况下都不起作用,不是吗?如果你这样做会怎么样?

cout << ((Polygon *)0x12345678)->area() << endl;

假设您被允许访问该随机内存区域。

我会测试一下,但我现在无法在计算机上测试。

(我希望我不会错过任何明显的东西......)

有帮助吗?

解决方案

每个对象(属于具有至少一个虚函数的类)都有一个指针,称为 vptr 。它指向其实际类的 vtbl (每个具有虚函数的类至少有一个;对于某些多继承方案,可能不止一个)。

vtbl 包含一堆指针,每个虚拟函数一个。所以在运行时,代码只使用对象的 vptr 来定位 vtbl ,并从那里找到实际被覆盖函数的地址。

在您的特定情况下, Polygon Rectangle Triangle 每个都有一个 vtbl ,每个都有一个条目指向其相关的 area 方法。您的 ppoly1 将指向 Rectangle vtbl ppoly2 类似于 Triangle vtbl 。希望这有帮助!

其他提示

Chris Jester-Young 给出了这个问题的基本答案。

维基百科有更深入的处理。

如果你想知道这类事物是如何工作的全部细节(以及所有类型的继承,包括多重和虚拟继承),最好的资源之一是Stan Lippman的“在C ++对象模型中“。

忽略绑定的各个方面,实际上并不是编译器决定了这一点。

C ++运行时通过vtable和vpointers评估派生对象在运行时实际上是什么。

我强烈推荐Scott Meyer的书“Effective C ++”,以便详细说明如何做到这一点。

甚至涵盖了如何忽略派生类中方法中的默认参数,并且仍然采用基类中的任何默认参数!那是有约束力的。

回答问题的第二部分:该地址可能没有正确位置的v-table,并且随之而来的是疯狂。此外,它根据标准未定义。

cout << ((Polygon *)0x12345678)->area() << endl;

此代码是等待发生的灾难。编译器可以正确编译它,但是当涉及到运行时,你将不会指向一个有效的v表,如果你很幸运,程序就会崩溃。

在C ++中,你不应该像这样使用旧的C风格的强制转换,你应该像这样使用 dynamic_cast

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area();
ASSERT(obj != NULL);

cout << obj->area() << endl;
如果给定的指针不是有效的Polygon对象,

dynamic_cast将返回NULL,因此它将被ASSERT捕获。

虚拟功能表。也就是说,两个Polygon派生对象都有一个虚函数表,其中包含指向所有(非静态)函数实现的函数指针。当你实例化一个Triangle时,area()函数的虚函数指针指向Triangle :: area()函数;当您实例化一个Rectangle时,area()函数指向Rectangle :: area()函数。由于虚函数指针与内存中对象的数据一起存储,因此每次将该对象作为Polygon引用时,都将使用该对象的相应area()。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top