代码之家  ›  专栏  ›  技术社区  ›  Paige Ruten

C++编译器如何知道调用虚拟函数的实现?

  •  9
  • Paige Ruten  · 技术社区  · 18 年前

    下面是一个来自 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是一个矩形而ppoly2是一个三角形,以便调用正确的area()函数?它可以通过查看“Polygon*ppoly1=&rect;”线并知道rect是一个矩形来发现这一点,但这在所有情况下都不起作用,不是吗?如果你做了这样的事怎么办?

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

    假设你被允许访问随机的内存区域。

    我想测试一下,但我不能在我现在用的电脑上。

    6 回复  |  直到 18 年前
        1
  •  25
  •   C. K. Young    18 年前

    每个对象(属于至少有一个虚函数的类)都有一个指针,称为 vptr . 它指向 vtbl

    这个 vtbl vptr 找到 ,然后是实际重写函数的地址。

    Polygon , Rectangle 和 Triangle 每个都有一个 ,每个条目都有一个指向其相关 area ppoly1 vptr 指向 长方形 的 vtbl 和 ppoly2 三角形 vtbl

        2
  •  6
  •   Community Mohan Dere    9 年前

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

    Wikipedia 有更深入的治疗。

    如果您想了解这类事情如何运作的全部细节(以及所有类型的继承,包括多重继承和虚拟继承),最好的资源之一是Stan Lippman的“ Inside the C++ Object Model

        3
  •  3
  •   Rob Wells    18 年前

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

    我强烈推荐Scott Meyer的书《有效C++》来说明如何做到这一点。

    甚至还介绍了如何忽略派生类中方法中的默认参数,以及如何仍然使用基类中的任何默认参数!那很有约束力。

        4
  •  1
  •   Zach Snow    18 年前

    回答你问题的第二部分:这个地址可能不会在正确的位置有一个v形桌,疯狂会接踵而至。而且,根据标准,它是未定义的。

        5
  •  1
  •   Adam Pierce    18 年前
    cout << ((Polygon *)0x12345678)->area() << endl;
    

    在C++中,不应该使用像这样的旧C样式转换,应该使用 动态浇铸

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

    如果给定指针不是有效的多边形对象,则dynamic_cast将返回NULL,因此它将被断言捕获。

        6
  •  1
  •   Paul Sonier    18 年前

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