【问题标题】:how c++ implements the polymorphism internally?c++ 如何在内部实现多态性?
【发布时间】:2010-01-31 08:49:03
【问题描述】:

尊敬的先生!

我应该告诉你我所知道的和我不知道的问题,这样你就可以解决我理解的薄弱环节。

我知道 C++ 通过使用指针数组 Vtable 来实现多态性 每个指针指向类的虚函数,层次结构中的每个类都有一个vtable。现在假设我有以下课程

class person
{
    char name[20];
public:
    person(char* pname)
    {
        strcpy(name,pname);
    }

    virtual void show()
    {
        cout<<"inside person show method, Name: "<<name;
    }
};

class teacher:public person
{
     int scale;

     teacher(char*pname, int s):person(pname)
     {
         scale=s;
     }

     void show()
     {
         cout<<"inside the teacher show method, Scale: "<<scale;
     }
};

现在假设我在主程序中编写

person *ptr;
ptr=new teacher(16,"Zia");
ptr->show();

现在我很困惑,调用将转到基类的 show 函数,现在因为它是一个虚函数,所以它又调用了适当的函数。我知道我在这里错了。我很困惑,呼叫的顺序是什么。 Vtable的作用是什么,它是如何工作的请详细说明。

【问题讨论】:

  • Zia,请学习 Markdown 语法:stackoverflow.com/editing-help
  • 这是在您的级别上需要了解的全部信息。更多地关注语言以及它是如何工作的,而不是它是如何实现的。这是只有在编写编译器时才需要知道的那种公开信息。最终你会了解基础知识,但就像下面的所有示例一样,大多数人只知道真正的基础知识,而不知道他们真正需要知道的细节。
  • @Martin 有些人需要了解管道才能使用浴室。我知道我知道。此外,即使 C++ 开发人员不编写编译器,了解 virtual 关键字的结果也很重要。
  • @Vincent 如果你打算来我家,请告诉我——我会准备好拖把和水桶。
  • @Martin:最初的提问者不需要知道答案并不意味着更高级的人不能从被问及回答中受益

标签: c++ oop


【解决方案1】:

由于showperson 类中声明为virtual,编译器不会像对非虚拟方法那样对方法调用进行硬编码,而是在V-表以便检索正确的函数。

所以ptr-&gt;show()会被编译成ptr-&gt;vtable['show'](),意思是“搜索对应方法show的函数指针并执行”。

由于在运行时,ptr 指向类 teacher 的对象,show 的 vtable 槽包含指向类 teacher 中方法 show 的指针。这就是执行正确方法的原因。

实际上,V表中的查找不是使用字符串而是使用数字方法标识符来完成的,以便尽可能快。

【讨论】:

    【解决方案2】:

    我认为您应该关注 Stanley B. Lippman 的书“Inside C++ object model”。

    让我们为您的课程寻找内部演示:

    人和老师的虚拟表

    |---------------|  +---> |------------------------|
    |  name         |  |     | "type_info" for person |
    |---------------|  |     |------------------------|
    |__vptr__person |--+     | "person::~person"      |
    |---------------|        |------------------------|
    person p;                | "person::show"         |
                             |------------------------|
    
    |----------------|  +---> |-------------------------|
    |person subobject|  |     | "type_info" for teacher |
    |----------------|  |     |-------------------------|
    |__vptr__teacher |--+     | "teacher::~teacher"     |
    |----------------|        |-------------------------|
    teacher t;                | "teacher::show"         |
                              |-------------------------|               
    

    一般来说,我们不知道每次调用 show() 时对象 ptr 地址的确切类型。然而,我们确实知道,通过 ptr 我们可以访问与对象的类关联的虚拟表。

    虽然我们不知道要调用哪个 show() 实例,但我们知道每个实例的地址都包含在 slot 2 中。

    此信息允许编译器在内部将调用转换为

    ( *ptr->vptr[ 2 ] )( ptr ); 
    

    在此转换中,vptr 表示插入每个类对象中的内部生成的虚拟表指针,2 表示 show() 在与 Point 层次结构关联的虚拟表中分配的插槽。我们在运行时唯一需要做的就是使用 RTTI 计算 ptr 的动态类型(和适当的 vtable)。

    【讨论】:

    • 他并没有把他的析构函数变成虚拟的
    • 是的。我认为 Zia ur Ra​​hman 的回答是:“亲爱的先生,这不是整个代码,我只发布了有问题的代码。”;)
    • 噢噢是的先生!你是对的,我只发布相关代码。因为人们讨厌阅读冗长的代码。是的,您的回答非常有帮助,谢谢。
    • 仅仅拥有一个函数索引并不能用于多重继承,不是吗?
    • RTTI 与 vtable 无关。尝试在您的编译器中禁用 RTTI,您的虚拟方法仍将正确解析。 RTTI 仅用于在运行时动态存储对象类型以及字段。
    【解决方案3】:

    C++ 语言没有定义多态实现,甚至没有定义vtable。这取决于编译器。
    一种可能的实现是Vincent Robert 提到的。

    【讨论】:

      【解决方案4】:

      标准没有说明如何实现多态性。一类一vtbl和一对象一vptr是最流行的方式。 我希望下面的伪代码会有所帮助。

      typedef struct {
          void (*show)(void* self);
          // more
      
      } person_vtbl;
      
      typedef struct {
            person_vtbl* vtbl;
            char         name[20];
      } person;
      
      void person_show(void* self) {
            cout<<"inside ... "<<static_cast<person*>(self)->name;
      }
      // more
      
      static person_vtbl person_vtbl_ = { &person_show }; // vtbl for person class
      
      void person_ctor(void* self, char const* name) {
            person* p = static_cast<person*>(self);
            strcpy(p->name, name);
            p->vtbl   = &person_vtbl  // vptr of person object
      }
      
      typedef struct {
          person base;
          int    scale;
      } teacher;
      
      void teacher_show(void* self) {
            cout<<"inside the tearch ... "<<static_cast<teacher*>(self)->scale;
      }
      static person_vtbl teacher_vtbl_ = { &teacher_show };
      
      void teacher_ctor(void* self, char const* name, int s) {
            teacher* t = static_cast<teacher*>(self);
            person_ctor(&t->base, name);     // construct base part
            t->scale   = s;                  // construct teacher part
            t->vtbl    = &teacher_vtbl_;     // vptr of teacher object
      }
      
      // construct teacher :
      // person* ptr = new teacher("Zia", 16);
      
      teacher* tmp = static_cast<teacher*>( malloc( sizeof *tmp );
      teacher_ctor(tmp, "Zia", 16);  // vptr of tmp points to teacher_vtbl_
      person* ptr = &tmp->base;
      
      // call virtual function though pointer
      // ptr->show()
      
      ptr->vptr->show(ptr); // call teacher_show(ptr);
      

      【讨论】:

      • 什么是“t->vtbl”?实际上 t 类没有成员 vtbl
      猜你喜欢
      • 2014-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多