1

我正在尝试使用 c++,试图理解继承并编写了以下代码:

#include <iostream>
#include <cstdlib>

class Base1{
public:
    virtual void print_hello() const
    { std::cout << "Base1: Hello!" << std::endl;}
};

class Base2{
public:
    virtual void print_hello() const
    { std::cout << "Base2: Hello!" << std::endl; }
};

class Derived: public Base1, public Base2
{
public:  
    virtual void print_hello() const
    { std::cout << "Derived: Hello!" << std::endl; }
};

int main() {    
Base1* pb1=new Derived;

pb1->print_hello();

delete pb1;

Base2* pb2=new Derived;

pb2->print_hello();

delete pb2;

return EXIT_SUCCESS;}

代码编译正常,但是当我运行它时,出现运行时错误:

Derived: Hello!
Derived: Hello!
*** glibc detected *** ./a.out: free(): invalid pointer: 0x0000000001b0c018 ***

后跟一个回溯和一个内存映射列表

两个 cout 语句都打印在屏幕上,所以我猜错误是在尝试删除 pb2 时产生的。

如果我不指定成员函数 virtual,代码运行正常。如果我在删除 pb1(即pb1=new Derived;)后重用 pb1,而不是创建新指针 pb2,该代码也可以正常运行。我在这里想念什么?

PS:我在 Ubuntu 12.04 中使用 g++ (4.6.4) 和 icc (2013.3.163) 尝试了代码

4

2 回答 2

1

您正在两个地方进入未定义行为的美妙世界:

delete pb1;
delete pb2;

这是未定义的行为,因为既没有析构函数,Base1Base2没有virtual析构函数,但您正在尝试delete通过基指针指向的对象。

您可能会惊讶于第一个实例 ( delete pb1) 也是未定义行为,因为它似乎有效。这就是未定义行为的美妙之处——任何事情都可能发生,甚至是您期望发生的事情。

作为一项规则,当使用多态时,你的基类应该总是有一个virtual析构函数。在许多情况下,这可能是微不足道的:

class Base1{
public:
    virtual void print_hello() const
    { std::cout << "Base1: Hello!" << std::endl;}
      virtual ~Base1() {}
};

class Base2{
public:
    virtual void print_hello() const
    { std::cout << "Base2: Hello!" << std::endl; }
      virtual ~Base2() {};
};

我还要指出,您的等级制度有些……不寻常。一般不需要多重继承。通常有更好的方法来完成你想做的事情。当您确实使用多重继承时,拥有多个具有同名成员函数的基类几乎总是一个设计缺陷。你通常会得到意想不到的(但定义明确的)行为。

于 2013-07-17T13:54:52.393 回答
0

通过指向基类型的指针删除派生类型的对象时,基类型必须具有虚拟析构函数。没有它,您将有未定义的行为。所以:添加一个虚拟析构函数到Base1和 到Base2.

于 2013-07-17T13:48:26.523 回答