Ошибка сегментации виртуального dtor - PullRequest
2 голосов
/ 25 сентября 2011

У меня есть следующий код в 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), поэтому я предполагаю, что проблема связана к размеру объекта, кто-нибудь может дать мне объяснение? Спасибо, Амит.

Ответы [ 3 ]

6 голосов
/ 25 сентября 2011
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 голосов
/ 25 сентября 2011

Вы не можете иметь полиморфные массивы в C ++.Массивы основаны на арифметике указателей, а арифметика указателей - на том, что компилятор знает размеры объектов.Любой доступ к любому элементу массива за пределами нуля в вашем случае не определен.

2 голосов
/ 25 сентября 2011

Я не совсем уверен, но это то, что я подозреваю ...

Интересно, связано ли это с тем, что массив производных классов не следует приводить к массиву базовых классов (они не совпадают, см. http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.3): как бы delete узнал размер удаляемого объекта, чтобы настроить указатель для numArr[0] и numArr[1] (чтобы найти v-таблицу и передать this в d-tor)

Очевидно, стандарт явно называет это неопределенным (5.3.5):

Во втором варианте (удаление массива), если динамический тип удаляемого объекта отличается от его статического типа, поведение не определено.

...