Передовая декларация просто не будет делать

StackOverflow https://stackoverflow.com/questions/4023794

  •  26-09-2019
  •  | 
  •  

Вопрос

Ниже приведены два фрагмента (готов к компиляции) кода. В первом фрагменте, в котором я использую только передовую декларацию для структуры во время удаления указателя на эту структуру из базового класса DTOR для гостевого класса, не вызывается.
Во втором фрагменте, когда вместо передовой декларации я использую полное определение этого гостевого класса, используя удаление в базовых работах 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;
}

// Второй - полный 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;
}
Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

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

Вы не можете удалить указатель на неполный тип. Удалить - это одна из операций, которые требуют завершенного типа. Емкость

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

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

(Заголовок 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.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top