理解虚拟和非虚函数调用有问题

时间:2013-05-08 05:17:10

标签: c++ virtual

我对以下代码段和输出有一些理解问题。任何人都可以提供一个解释,主要是为什么test()按照输出中的方式工作。我正在使用MSCV 2008 C ++编译器。

class AS
{
    int a;

public:
    AS():a(1){show();}
    virtual void show() {cout<<a<<endl;}
    void test() { cout<<"Calling show()"<<endl; this->show();}
};

class BS: virtual public AS
{
    int b;
public:
    BS():b(2){show();}
    virtual void show() {cout<<b<<endl;}
    void test() { cout<<"Calling show()"<<endl; this->show();}
};

class CS:public virtual  AS
{
    int c;
public:
    CS():c(3){show();}
    virtual void show() {cout<<c<<endl;}
    void test() { cout<<"Calling show()"<<endl; this->show();}
};

class DS:BS, public CS
{
    int d;
public:
DS():d(4){show();}
    virtual void show() {cout<<d<<endl;}
    void test() { cout<<"Calling show()"<<endl; this->show();}
};

int main()
{
cout<<"Class Sizes:"<<endl;
cout<<sizeof(AS)<<endl;
cout<<sizeof(BS)<<endl;
cout<<sizeof(CS)<<endl;
cout<<sizeof(DS)<<endl;

AS* aa = new DS();  
aa->test();
aa->show();

delete aa;

return 0;
}

输出是: -

Class Sizes:
8
20
20
32
1
2
3
4
Calling show()
4
4

和删除aa时的断点异常;为什么?

2 个答案:

答案 0 :(得分:5)

每当您在指向派生类对象的基类指针上调用delete时,基类必须具有virtual析构函数。如果不这样做会导致未定义的行为

因此,您的班级AS需要提供virtual析构函数:

class AS
{
    public:
        virtual ~AS(){}
};

您的混淆似乎是通过构造函数和&amp ;;调用的virtual函数的输出。析构函数。

构造函数和析构函数中this类型是正在调用构造函数/析构函数的类的类型。因此,构造函数和析构函数中的任何virtual函数调用都不会显示您通常期望的虚函数的动态调度行为。而是调用该特定类的函数。


对于size类对象。你不应该假设尺寸是特定的。编译器可以自由添加填充字节,这可能会增加甚至非多态类的大小。对于多态类,通常大多数实现将向类对象添加虚拟指针以实现动态分派机制,从而增加对象大小。请注意,这完全取决于实现 因此,始终只需使用sizeof获取大小,并且永远不要依赖它是任何特定的。

答案 1 :(得分:0)

这是我的一点,如果我错了,请纠正我。

[注意:vptr-vtable机制用于实现虚函数调用,而vbptr(虚基类指针)用于实现虚基类。     此外,sizeOf(某些多态类)可能会因使用的编译器+平台而有所不同]

1)类AS的实例需要8个字节      (“int a”为4个字节,隐藏vptr为4个字节)= 8

2)BS类的实例需要20个字节

(4字节保存基类AS + 4字节填充+ 4字节表示“int b”+ 4字节表示隐藏vptr + 4字节表示vbptr)= 20

3)类CS的实例需要20个字节

(4字节保存基类AS + 4字节填充+ 4字节表示“int c”+ 4字节表示隐藏的vptr + 4字节表示vbptr)= 20

4)类DS的实例需要32个字节

(4个字节用于保存共享基类AS + 4个字节用于“int d”+     8个字节用于保存基类BS(占成员大小+ vbptr大小)+ 8个字节用于保存基类CS(占成员大小+ vbptr大小)+ 4个字节用于隐藏DS :: vptr + 4个字节用于DS :: vbptr )= 32

[请注意,在实现vptr-vtable机制时,编译器使用虚函数地址扩充vtable,在DS类的内存模型中存在单个v-table和单个v-ptr。然而,vbptr将在每个虚拟基类中以及从它们继承的内部类中重复。

同样,所有这些都是编译器特定的实现,并且可能因编译器而异。

此外,将所有基类析构函数定义为虚拟以消除崩溃。