2011-09-25 97 views
2

我有下面的代码在C++:虚拟析构函数分段错误

#include <iostream> 

class Number 
{ 
public: 
    virtual void foo(){std::cout << "Number foo\n";}; 
    Number(){ std::cout << "Number ctor" << std::endl;} 
    virtual ~Number(){ std::cout << "Number dtor" << std::endl;} 
}; 


class Complex : public Number 
{ 
public: 
    virtual void foo(){std::cout << "Complex foo\n";}; 
    Complex (double r=0, double i=0) : _r (r), _i (i) 
    { std::cout << "Complex ctor" << std::endl; }; 
    virtual ~Complex(){ std::cout << "Complex dtor" << std::endl;} 
private: 
    double _r,_i; 
}; 


int main() 
{ 
    Number *numArr = new Complex [2]; 
    delete [] numArr; 
    return 0; 
} 

当析构函数声明为虚拟,应用与分段故障退出。当它没有被声明为虚拟的时候,比调用Number类的析构函数(这是显而易见的...)。但是,当析构函数被声明为虚拟的时,并且当我在Complex类中移除双精度时,没有分段错误,并且按照预期顺序(Complex,Number)调用析构函数,所以我想这个问题是相关的对象的大小,任何人都可以给我一个解释? 谢谢, 阿米特。

+0

@AndersK。这应该无处不在。 –

+0

该代码展示未定义的行为,并且问题是重复的:http://stackoverflow.com/questions/7197677/base-pointer-to-array-of-derived-objects – AraK

+0

我似乎每天都在学习新的东西。仍然。 –

回答

2

我不能完全肯定,但是这是我怀疑...

我不知道这是关系到一个事实,即派生类的数组不应该被强制转换为基类的数组(他们是不一样的,请参阅:http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.3):delete将如何知道它正在删除的对象的大小,以调整指针numArr[0]numArr[1](查找v表并将this传递给d-tor)

显然,标准明确地将其命名为undefined(5.3.5):

在第二个选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的。

+0

Nawaz击败了我相关的标准引用。 – Thanatos

6
Number *numArr = new Complex [2]; 
delete [] numArr; 

实际上,删除操作调用未定义的行为。

§5.3.5/ 3说,

在第一种方式(删除对象),如果静态类型的操作数是从它的动态类型不同,静态类型应是基类的操作数的动态类型和静态类型应该具有虚拟析构函数或行为未定义。 在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的。

这实际意味着是这样的:

Number *object= new Complex(); 
delete object; //well-defined 

//BUT 
Number *array = new Complex[N]; 
delete [] array; //undefined 
4

你不能多态数组在C++中。数组依赖于指针算术和指针算术依赖于编译器知道对象的大小。在你的情况下,任何对数组中任何元素的访问都是未定义的。

+0

我最喜欢这个解释。我不太了解其他答案,但是这个让我说“难懂!” – Marlon