1

我对以下代码段和输出有一些理解问题。任何人都可以提供一个解释,主要是为什么 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的断点异常;为什么 ?

4

2 回答 2

5

每当您调用delete指向派生类对象的基类指针时,基类必须具有virtual析构函数。不这样做会导致Undefined Behavior

所以,你的类AS需要提供一个virtual析构函数:

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

您的困惑似乎是virtual通过构造函数和析构函数调用的函数的输出。

构造函数和析构函数内的类型调用构造函数/析构函数的类的类型。因此,来自构造函数和析构函数的任何函数调用都不会显示您通常期望的虚函数的动态调度行为。相反,该特定类的函数被调用。thisvirtual


至于尺寸类对象。您不应该假设尺寸是特定的。编译器可以自由添加填充字节,这可能会增加非多态类的大小。对于多态类,通常大多数实现都会添加一个指向类对象的虚拟指针来实现动态分派机制,从而增加对象大小。请注意,这完全取决于实现。
所以总是只使用尺寸sizeof,不要依赖它来做任何具体的事情。

于 2013-05-08T05:28:59.950 回答
0

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

[注:vptr-vtable机制用于实现虚函数调用,vbptr(virtual base class pointer)用于实现虚基类。此外,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 将在每个虚拟基类和从它们继承的类中复制]。

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

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

于 2013-05-08T05:53:55.153 回答