虚函数和类

时间:2010-06-23 07:22:34

标签: c++

我需要一些基本问题的答案。我又迷了路。 :(

q1 - 此声明是否有效:
Whenever we define the function to be pure virtual function, this means that function has no body.

q2 - 动态绑定的概念是什么?我的意思是如果编译器使用VTABLE和VPTR优化代码那么它是如何运行时多态性的?

q3 - 什么是VTABLES和VPTR以及它们的尺寸如何变化?

q4 - 请参阅此代码:

class base
{
    public:
        virtual void display()
        {
            cout<<"Displaying from base";
        }
};

class derived:public base
{
    public:
        void display(){cout<<"\nDisplaying from derived";}
};

int main()
{
    base b,*bptr;
    derived d;
    bptr=&b;
    bptr->display();
    bptr=&d;
    bptr->display();
} 

输出:

Displaying from base
Displaying from derieved

请有人回答为什么基类的指针可以指向派生类的成员函数而反之亦然,为什么?

4 个答案:

答案 0 :(得分:4)

  1. 假。它只是意味着任何派生类必须实现所述函数。您仍然可以为该函数提供定义,并且可以通过Base::Function()调用它。*

  2. 虚拟表是实现虚拟功能的一种方式。 (但标准并没有强制要求这是方法。)在进行多态调用时,编译器将在函数表中查找函数并调用该函数,从而启用运行时绑定。 (该表在编译时生成。)

  3. 见上文。它们的大小随着虚拟功能的增加而变化。但是,实例不存储表,而是存储指向表的指针,因此类大小仅增加一个大小。

  4. Sounds like you need a book.

  5. <子> *这方面的一个典型例子是:

    struct IBase
    {
        virtual ~IBase(void) = 0;
    };
    
    inline IBase::~IBase(void) {}
    

    这不是没有纯虚函数的抽象类,但是析构函数需要一个定义(因为它会在派生类破坏时被调用。)

答案 1 :(得分:2)

1)不一定。有时您为纯虚函数提供正文

2)要调用的函数在运行时确定。

答案 2 :(得分:1)

  1. 假。它只表示派生类必须实现该方法,并且该级别的方法定义(如果存在)不会被视为重写虚拟方法。
  2. vtable在编译时实现,但在运行时使用。编译器将通过vtable重定向调用,这取决于对象的运行时类型(指向base的指针具有静态类型base*,但可能在运行时指向类型为derived的对象)
  3. vptrs是指向vtable的指针,它们不会改变大小。 vtables是指向代码的指针表(可能指向方法或某些适配器代码),并为类中声明的每个虚方法提供一个条目。
  4. 在代码中编辑后:

    指针在第一次调用期间引用类型base的对象,但它指向第二个调用位置处类型为derived的对象。动态调度机制(vtable)将调用路由到适当的方法。

    一个常见的实现,它可以帮助您理解,在声明虚函数的每个类中,编译器为指向虚拟表的指针保留空间,并且它还生成虚拟表本身,在其中添加指向定义的虚拟表。每个虚拟方法。对象的内存布局只有那个额外的指针。

    当派生类重写任何基类方法时,编译器会生成一个不同的vtable,其指针指向该级别的最终重写器。基类和派生类的内存布局在base子对象部分(通常是开头)中重合,但base对象的vptr值将指向基本vtable,而derived对象中vptr的值将指向派生的vtable。

    当编译器看到类似bptr->display()的调用时,它会检查基类的定义,并发现它是第一个虚方法,然后将调用重定向为:bptr->hidden_vptr[0]()。如果指针指的是真实的基本实例,那么它将指向base::display,而在derived实例的情况下,它将指向derived::display

    请注意,在这个答案中有很多挥手。所有这些都是实现定义的(语言没有指定调度机制),并且在大多数情况下,调度机制更复杂。例如,当发生多重继承时,vtable不会直接指向最终的覆盖,而是指向重置第一个隐式this参数偏移的代码的适配器块,作为所有的base子对象但是第一个基础与内存中派生最多的对象没有对齐 - 这远远超出了问题的范围,只记得这个答案是一个粗略的想法,并且在实际系统中增加了复杂性。

答案 3 :(得分:1)

  

此声明是否有效

不完全是:它可能有一个身体。更准确的定义是“每当我们将方法定义为纯虚拟时,这意味着必须在具体的子类中定义(覆盖)该方法。”

  

动态绑定的概念是什么?我的意思是如果编译器使用VTABLE和VPTR优化代码,那么它是如何运行时多态的?

如果在运行时有超类(例如Shape)的实例,则不需要/不必知道它的哪个子类(例如Circle或Square)。

  

什么是VTABLES和VPTR以及它们的尺寸如何变化?

每个类有一个vtable(对于任何具有一个或多个虚方法的类)。 vtable包含指向类的虚方法地址的指针。

每个对象有一个vptr(对于任何具有一个或多个虚方法的对象)。 vptr指向该对象类的vtable。

vtable的大小随着类中虚函数的数量而增加。 vptr的大小可能是不变的。

  

请有人回答为什么基类的指针可以指向派生类的成员函数而反之亦然,为什么?

如果你想调用基类函数(因为它是虚拟的,而virtual的默认行为是通过vptr / vtable调用派生最多的版本),那么你必须明确地说,例如像这样:

bptr->base::display();