C ++: auto_ptr + форвардное объявление?

У меня есть такой класс:

class Inner;

class Cont
{
public:
    Cont();
    virtual ~Cont();
private:
    Inner* m_inner;
};

в .cpp конструктор создает экземпляр Inner с new, а деструктор delete его. Это работает очень хорошо.
Теперь я хочу изменить этот код, чтобы использовать auto_ptr, поэтому я пишу:

class Inner;

class Cont
{
public:
    Cont();
    virtual ~Cont();
private:
    std::auto_ptr<Inner> m_inner;
};

Теперь конструктор инициализировал auto_ptr, а деструктор ничего не делает.

Но это не работает. проблема, кажется, возникает, когда я создаю экземпляр этого класса. Я получаю это предупреждение:

предупреждение C4150: удаление указателя на неполный тип «Внутренний»; деструктор не вызывается

Что ж, это, очевидно, очень плохо, и я понимаю, почему это происходит. Компилятор не знает о d'tor Inner при создании экземпляра шаблона auto_ptr<Inner>

Итак, мой вопрос: есть ли способ использовать auto_ptr с предварительным объявлением, как я сделал в версии, использующей простые указатели?
Необходимость #include для каждого класса, на который я объявляю указатель, является огромной проблемой, а иногда и просто невозможной . Как обычно решается эта проблема?


person shoosh    schedule 23.12.2009    source источник
comment
Раньше у меня возникала эта проблема, когда я объявлял пустой деструктор (или не объявлял его вообще) в файле заголовка класса Cont. Судя по вашему примеру, вы, кажется, делаете это правильно, но просто хотели выделить эту проблему.   -  person rui    schedule 23.12.2009
comment
D'tor объявлен в cpp, и я получаю предупреждение о создании экземпляра auto_ptr внутри класса.   -  person shoosh    schedule 23.12.2009


Ответы (7)


Вам необходимо включить заголовок, определяющий class Inner, в файл, где находится Cont::~Cont() реализация. Таким образом, у вас все еще есть предварительное объявление в заголовке, определяющее class Cont, а компилятор видит определение class Inner и может вызвать деструктор.

//Cont.h
class Inner; // is defined in Inner.h
class Cont 
{ 
    virtual ~Cont(); 
    std::auto_ptr<Inner> m_inner;
};

// Cont.cpp
#include <Cont.h>
#include <Inner.h>

Cont::~Cont()
{
}
person sharptooth    schedule 23.12.2009
comment
Я делаю это, но предупреждение указывает на то, что Inner необходимо определить в строке создания auto_ptr внутри класса. - person shoosh; 23.12.2009
comment
Это очень странно. Это Visual C ++? - person sharptooth; 23.12.2009
comment
Вышеупомянутое решение в точности соответствует тому, как мы это делаем в VS2k3, VS2k5 и VS2k8. Очень-очень странно. Возможно, вы могли бы включить генерацию препроцессированного файла и изучить его. - person sharptooth; 23.12.2009
comment
@sharptooth Для ясности: определение деструктора не всегда необходимо. Единственное, что важно, это то, что определение Inner должно быть доступно в момент уничтожения Cont. Кроме того, нет необходимости в virtual (в) деструкторе. - person amit; 21.12.2011
comment
@phaedrus: Да, за исключением виртуального деструктора, вы все равно можете привязать auto_ptr<Base> к Derived* и запустить в UB, а отсутствие определения класса в точке разрушения объекта также является UB и, следовательно, не требует диагностики компилятором. - person sharptooth; 21.12.2011

Оказывается, проблема возникает только тогда, когда я делаю c'tor встроенным. Если я поставлю c'tor в cpp, после отклонения Inner все будет в порядке.

person shoosh    schedule 06.06.2011

Вместо этого вы можете рассмотреть boost :: shared_ptr (). У него нет практических недостатков вместо производительности, и он намного удобнее пересылать объявления:

boost::shared_ptr<class NeverHeardNameBefore> ptr;

это нормально, без лишних заявлений выше.

shared_ptr делает больше, чем auto_ptr, например подсчет ссылок, но он не должен навредить, если он вам не нужен.

person Pavel Radzivilovsky    schedule 23.12.2009
comment
Или boost::scoped_ptr, что ближе, за исключением того, что он даже не пытается притвориться, что ваши экземпляры можно будет копировать. Но тогда он все еще не отвечает на вопрос, как их использовать с неполными типами (я думаю, вам нужно будет определить деструктор к тому времени, когда тип будет завершен). - person UncleBens; 23.12.2009

Это кажется смешным, но я решил ту же проблему, добавив #include <memory> в файл Cont.h.

person Antern    schedule 05.12.2012

Прямое объявление в заголовке в порядке, если вы реализуете деструктор в файле cont.cpp и включаете inner.h, как указывали другие.

Проблема могла быть в использовании Cont. В каждый cpp, который использует (и уничтожает) Cont, вы должны включать cont.h И inner.h. Это решило проблему в моем случае.

person jjth    schedule 15.06.2011

Этот вопрос (удаление объекта с помощью частного деструктора) и этот вопрос (как написать iscomplete шаблон) может вам помочь.

person Alexey Malistov    schedule 23.12.2009

Технически вы не должны создавать экземпляры стандартных библиотечных шаблонов с неполными типами, хотя я не знаю ни одной реализации, в которой это не сработает. На практике я бы порекомендовал и Sharptooth.

На самом деле нет ничего плохого в использовании голого указателя для указателя на impl, если вы вызываете для него delete в своем деструкторе. Вероятно, вам также следует реализовать или отключить конструктор копирования и оператор присваивания.

person Brian Neal    schedule 24.12.2009