2009-10-23 1 views
9

Как возможно, что разрешено удалять объект с помощью частного деструктора в следующем коде? Я уменьшил реальную программу до следующего образца, но он все еще компилируется и работает.Удаление объекта с помощью частного деструктора

class SomeClass; 

int main(int argc, char *argv[]) 
{ 
    SomeClass* boo = 0; // in real program it will be valid pointer 
    delete boo; // how it can work? 

    return -1; 
} 

class SomeClass 
{ 
private: 
    ~SomeClass() {}; // ! private destructor ! 
}; 
+0

Interesting..if Я двигаюсь определение класса выше основной(), то он выдает ошибку компиляции. В противном случае я просто получаю предупреждение C4150: удаление указателя на неполный тип «SomeClass»; нет деструктора, называемого « – Naveen

+1

@Naveen: Ожидается. Неполный тип - одна проблема. Частный деструктор - другой. Каждый из них имеет собственное диагностическое сообщение. Вы переключаетесь между ними, перемещая определение 'SomeClass'. – AnT

ответ

15

Вы пытаетесь удалить объект неполного типа класса. C++ Standard говорит, что вы получите неопределенное поведение в этом случае (5.3.5/5):

Если объект удаляется имеет неполный тип класса в точке удаления и полного класса имеет нетривиальный деструктор или функция удаления, поведение не определено.

Для выявления таких случаев можно использовать boost::checked_delete:

template<typename T> 
inline void checked_delete(T* p) 
{ 
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; 
    (void) sizeof(type_must_be_complete); 
    delete p; 
} 
+1

Здесь используется функция sizeof(), которая никогда не возвращает 0 даже для пустых структур/классов, которые я забыл. Раздел 9 ISO-IEC-14882.1: «Завершенные объекты и субобъекты классов типа класса должны иметь ненулевой размер». Подразумевается, что каждый объект в массиве будет иметь уникальный адрес памяти. Предположительно, для структур - это так и в C? – leander

+0

В C размер пустой структуры равен 0. –

+0

@leander: Не совсем. Вы видите, что на C++ просто * незаконно * применять 'sizeof' к неполному типу. Именно этот код пытается поймать '. Это выражение прекращает компиляцию на компиляторе, который соответствует стандарту путем выдачи * ошибки *. Однако, если какой-то странный компилятор разрешает 'sizeof' на неполных типах (как расширение) с результатом 0, то часть' typedef ... 'поймает их и заставит« массив отрицательного размера »выйти из строя. Итак, у вас есть главная «ловушка» здесь (часть '(void)» и вспомогательная ловушка «на всякий случай» (часть 'typedef'). – AnT

7

Этот код вызывает неопределенное поведение (UB). Это UB в C++ - delete - объект неполного типа, имеющий нетривиальный деструктор. И в вашем коде тип SomeClass является неполным в точке delete, и он имеет нетривиальный деструктор. Компиляторы обычно выдают предупреждение об этом, поскольку на C++ формально это не является нарушением ограничения.

Итак, строго говоря, ваш код не работает. Это просто компилирует и делает что-то не определено при запуске.

Компилятор просто не обязан улавливать эту ошибку. Причина этого в том, что это может быть совершенно нормально, если у вашего объекта есть тривиальный дескриптор. Компилятор не знает, какой деструктор этого типа в конечном итоге будет иметь, поэтому он не может точно сказать, является ли это ошибкой или нет.

+0

Было бы хорошо, если (1) 'T' имеет тривиальный деструктор (2)' T' не переопределяет 'delete operator'' – curiousguy

4

Поскольку тип SomeClass не объявлен полностью при вызове operator delete.

Удаление такого указателя является неопределенным поведением, но на практике большинство компиляторов просто освобождают память (если указатель не был NULL) и не вызывают деструктор.

Например, г ++ даст вам предупреждение об этой проблеме:

foo.cpp: In function 'int main(int, char**)': 
foo.cpp:6: warning: possible problem detected in invocation of delete operator: 
foo.cpp:5: warning: 'boo' has incomplete type 
foo.cpp:1: warning: forward declaration of 'struct SomeClass' 
foo.cpp:6: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined. 
+1

« Удаление такого указателя просто освободит память ... »- НЕТ! Поведение * НЕ УКАЗАНО *, то есть любые предположения о том, что происходит «по существу», являются недопустимыми. – DevSolar

+1

@DevSolar: Правда, он не определен по стандарту. На практике это будет происходить на большинстве компиляторов. Я обновил ответ, чтобы отразить это. – laalto

+0

У меня есть какая-то проблема, когда я полагаюсь на что-либо, находящееся за пределами хорошо управляемого мира. Особенно в коде коллег. Это подталкивает меня к стене. Я обвиняю такие ответы, как ваши. (;-) << - важный смайлик) – DevSolar