Форвардная декларация просто не сработает - PullRequest
6 голосов
/ 26 октября 2010

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

//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;
}

// Второй - полный def

#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;
}

Ответы [ 6 ]

16 голосов
/ 26 октября 2010

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

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

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

3 голосов
/ 26 октября 2010

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

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

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

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

3 голосов
/ 26 октября 2010

Нельзя удалить указатель на неполный тип.Удалить - это одна из операций, которая требует завершения типа.НТН

3 голосов
/ 26 октября 2010

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

2 голосов
/ 26 октября 2010

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

 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.

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

2 голосов
/ 26 октября 2010

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

...