2010-10-26 3 views
5

Ниже представлены два фрагмента (готовые к компиляции) кода. В первом фрагменте, в котором я использую только декларацию forward для структуры, при удалении указателя на эту структуру из базового класса dtor для класса Guest не вызывается.
Во втором фрагменте, когда вместо объявления forward я использую полное определение этого класса Guest, используя delete в Base works ase.
Почему? Почему это имеет значение? Разве декларация не является просто запиской для компилятора, говорящей, что определение этого класса/структуры находится где-то в другом месте?
Я очень удивлен, что он просто не работает интуитивно.Вперед декларация просто не будет

//First just forward dclr 
#include "stdafx.h" 
#include <iostream> 
using std::cout; 

struct Guest; 

struct Base 
{ 
    Guest* ptr_; 
    Base(Guest* ptr):ptr_(ptr) 
    { 
     cout << "Base\n"; 
    } 
    ~Base() 
    { 
     cout << "~Base\n"; 
     delete ptr_; 
    } 
}; 

struct Guest 
{ 
    Guest() 
    { 
     cout << "Guest\n"; 
     throw std::exception(); 
    } 
    Guest(int) 
    { 
     cout << "Guest(int)\n"; 
    } 
    ~Guest() 
    { 
     cout << "~Guest\n"; 
    } 
}; 

struct MyClass : Base 
{ 
    Guest g; 
    MyClass(Guest* g):Base(g) 
    { 
     cout << "MyClass\n"; 

    } 
    ~MyClass() 
    { 
     cout << "~MyClass\n"; 
    } 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    try 
    { 
     Guest* g = new Guest(1); 
    MyClass mc(g); 
    } 
    catch(const std::exception& e) 
    { 
     std::cerr << e.what(); 
    } 
    return 0; 
} 

// Второй - полный Защиту

#include "stdafx.h" 
#include <iostream> 
using std::cout; 

struct Guest 
{ 
    Guest() 
    { 
     cout << "Guest\n"; 
     throw std::exception(); 
    } 
    Guest(int) 
    { 
     cout << "Guest(int)\n"; 
    } 
    ~Guest() 
    { 
     cout << "~Guest\n"; 
    } 
}; 

struct Base 
{ 
    Guest* ptr_; 
    Base(Guest* ptr):ptr_(ptr) 
    { 
     cout << "Base\n"; 
    } 
    ~Base() 
    { 
     cout << "~Base\n"; 
     delete ptr_; 
    } 
}; 



struct MyClass : Base 
{ 
    Guest g; 
    MyClass(Guest* g):Base(g) 
    { 
     cout << "MyClass\n"; 

    } 
    ~MyClass() 
    { 
     cout << "~MyClass\n"; 
    } 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    try 
    { 
     Guest* g = new Guest(1); 
    MyClass mc(g); 
    } 
    catch(const std::exception& e) 
    { 
     std::cerr << e.what(); 
    } 
    return 0; 
} 
+0

Вы должны сделать ваши конструкторы базового класса виртуальными в обоих случаях. –

+0

@ Space_C0wb0y: Дорогой, вы не можете сделать конструктор виртуальным в C++ :) –

+0

@Armen: Слова ... дым и зеркала. Это было так ясно в моем сознании. (Для тех, кто задается вопросом, это должны быть * деструкторы *) –

ответ

3

Неофициально: компилятору требуется определение класса для правильного удаления объекта, поскольку ему необходимо знать, как вызвать деструктор и/или operator delete для этого класса.

Формально 5.3.5/5:

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

Было бы хорошо, если (например) Guest был POD, но вы дали ему деструктор, так что вы не в порядке.

3

Вы не можете удалять Гостя, если вы не знаете его определение. Это деструктор не будет вызван. Кроме того, если Гость определил пользовательский оператор delete, он будет проигнорирован.

+0

декларация -> определение :) –

+0

Нет, декларации достаточно. Однако форвардная декларация не будет делать. –

+0

@ kotlinski - 'class X {void f(); }; это определение X, тогда как «класс X» является объявлением –

16

От C++ стандарт (5.3.5/5):

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

Таким образом, вы не можете использовать удаление по вашему незавершенному типу. Это вызовет деструктор, и компилятор еще не знает об этом.

3

Вы не можете удалить указатель на неполный тип. Удалить - одна из операций, которая требует, чтобы тип был полным. HTH

2

Тип ptr_ является неполным, когда вы вызываете на него delete. Это приводит к неопределенному поведению. Таким образом, ваш деструктор не может быть вызван. Вы можете использовать Boost.checked_delete, чтобы избежать таких сценариев.

2

(. Stdafx.h заголовок не является стандартным C++) Если я компилирую с г ++, компилятор генерирует:

warning: possible problem detected in invocation of delete operator: 
warning: invalid use of incomplete type ‘struct Guest’ 
warning: forward declaration of ‘struct Guest’ 
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined. 

Настройте ваш компилятор для компиляции на соответствующих уровнях предупреждения и ошибки.

+1

int _tmain (int argc, _TCHAR * argv []) <- это также не стандартно –

Смежные вопросы