У меня есть код в заголовке, который выглядит следующим образом:
#include <memory>
class Thing;
class MyClass
{
std::unique_ptr< Thing > my_thing;
};
Если я включаю этот заголовок в cpp, который не включает Thing
определение типа, то он не компилируется под VS2010-SP1:
1> C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ include \ memory (2067): ошибка C2027: использование неопределенного типа 'Thing'
Замените std::unique_ptr
на, std::shared_ptr
и он компилируется.
Итак, я предполагаю, что текущая реализация VS2010 std::unique_ptr
требует полного определения и полностью зависит от реализации.
Либо это? Есть ли в его стандартных требованиях что-то, что делает невозможным для std::unique_ptr
реализации ее работу только с предварительным объявлением? Это кажется странным, поскольку он должен содержать только указатель Thing
, не так ли?
shared_ptr
/unique_ptr
» Говарда Хиннанта . Таблица в конце должна ответить на ваш вопрос.Ответы:
Принято отсюда .
Большинство шаблонов в стандартной библиотеке C ++ требуют, чтобы они были созданы с законченными типами. Однако
shared_ptr
иunique_ptr
являются частичными исключениями. Некоторые, но не все их члены могут быть созданы с неполными типами. Мотивация для этого заключается в поддержке идиом, таких как pimpl, с использованием умных указателей и без риска неопределенного поведения.Неопределенное поведение может возникнуть, когда у вас есть неполный тип, и вы вызываете
delete
его:Выше юридический код. Это скомпилируется. Ваш компилятор может выдавать или не выдавать предупреждение для приведенного выше кода, как указано выше. Когда это выполнится, плохие вещи, вероятно, произойдут. Если вам очень повезет, ваша программа потерпит крах. Однако более вероятным результатом является то, что ваша программа будет молча утекать память, как
~A()
не будет вызвано.Использование
auto_ptr<A>
в приведенном выше примере не помогает. Вы по-прежнему получаете такое же неопределенное поведение, как если бы вы использовали необработанный указатель.Тем не менее, использование неполных классов в определенных местах очень полезно! Это где
shared_ptr
иunique_ptr
помочь. Использование одного из этих умных указателей позволит вам избежать неполного типа, за исключением случаев, когда необходимо иметь полный тип. И самое главное, когда необходимо иметь полный тип, вы получите ошибку во время компиляции, если попытаетесь использовать интеллектуальный указатель с неполным типом в этой точке.Нет больше неопределенного поведения:
Если ваш код компилируется, то вы использовали полный тип везде, где вам нужно.
shared_ptr
иunique_ptr
требуют полный тип в разных местах. Причины неясны, связанные с динамическим или статическим удалением. Точные причины не важны. На самом деле, в большинстве кодов вам не очень важно точно знать, где требуется полный тип. Просто код, и если вы ошиблись, компилятор скажет вам.Тем не менее, в случае, если это полезно для вас, вот таблица, которая документирует несколько членов
shared_ptr
и вunique_ptr
отношении требований к полноте. Если элемент требует полного типа, тогда запись имеет «C», в противном случае запись таблицы заполняется «I».Любые операции, требующие преобразования указателей, требуют полных типов для обоих
unique_ptr
иshared_ptr
.unique_ptr<A>{A*}
Конструктор может уйти с неполнымA
только если компилятор не требуется , чтобы установить вызов~unique_ptr<A>()
. Например, если вы положили вunique_ptr
кучу, вы можете уйти с неполнымA
. Подробнее по этому вопросу можно найти в BarryTheHatchet в ответ здесь .источник
unique_ptr
переменную-член класса, просто явно объявите деструктор (и конструктор) в объявлении класса (в файле заголовка) и перейдите к их определению. в исходном файле (и поместите заголовок с полным объявлением указанного класса в исходном файле), чтобы предотвратить автоматическое встраивание компилятором конструктора или деструктора в файл заголовка (который вызывает ошибку). stackoverflow.com/a/13414884/368896 также помогает напомнить мне об этом.Компилятору нужно определение Thing для генерации деструктора по умолчанию для MyClass. Если вы явно объявите деструктор и перенесете его (пустую) реализацию в файл CPP, код должен скомпилироваться.
источник
MyClass::~MyClass() = default;
в файле реализации менее вероятно, что он будет случайно удален позже в будущем кем-то, кто полагает, что тело деструктора было стерто, а не намеренно оставлено пустым.default
ed иdelete
d.MyClass::~MyClass() = default
, не перемещать его в файл реализации на Clang. (еще?)Это не зависит от реализации. Это работает потому, что
shared_ptr
определяет правильный деструктор для вызова во время выполнения - он не является частью сигнатуры типа. Тем не менее,unique_ptr
деструктор является частью своего типа, и он должен быть известен во время компиляции.источник
Похоже, текущие ответы не совсем точно объясняют, почему конструктор по умолчанию (или деструктор) является проблемой, а пустые, объявленные в cpp, - нет.
Вот что происходит:
Если внешний класс (т.е. MyClass) не имеет конструктора или деструктора, компилятор генерирует классы по умолчанию. Проблема в том, что компилятор по существу вставляет пустой конструктор / деструктор по умолчанию в файл .hpp. Это означает, что код для стандартного конструктора / деструктора компилируется вместе с двоичным файлом исполняемого файла хоста, а не с двоичными файлами вашей библиотеки. Однако это определение не может реально создавать частичные классы. Поэтому, когда компоновщик входит в двоичный файл вашей библиотеки и пытается получить конструктор / деструктор, он не находит ничего, и вы получаете ошибку. Если код конструктора / деструктора был в вашем .cpp, то в бинарном файле вашей библиотеки он есть для связывания.
Это не имеет ничего общего с использованием unique_ptr или shared_ptr, и другие ответы, возможно, приводят к путанице в старой версии VC ++ для реализации unique_ptr (VC ++ 2015 отлично работает на моей машине).
Мораль этой истории в том, что ваш заголовок должен быть свободен от любого определения конструктора / деструктора. Он может содержать только их декларацию. Например,
~MyClass()=default;
в hpp не будет работать. Если вы позволите компилятору вставлять конструктор или деструктор по умолчанию, вы получите ошибку компоновщика.Еще одно замечание: если вы все еще получаете эту ошибку даже после того, как у вас есть конструктор и деструктор в файле cpp, то, скорее всего, причина в том, что ваша библиотека не компилируется должным образом. Например, однажды я просто изменил тип проекта с консоли на библиотеку в VC ++, и я получил эту ошибку, потому что VC ++ не добавил символ препроцессора _LIB, и это выдало точно такое же сообщение об ошибке.
источник
Просто для полноты:
Заголовок: Ах
Источник A.cpp:
Определение класса B должно просматриваться конструктором, деструктором и всем, что может неявно удалить B. (Хотя конструктор не указан в списке выше, в VS2017 даже конструктору требуется определение B. И это имеет смысл при рассмотрении что в случае исключения в конструкторе unique_ptr снова уничтожается.)
источник
Полное определение Thing требуется в момент создания шаблона. Это точная причина, почему идиома pimpl компилируется.
Если это не удалось, люди не будут задавать вопросы , как это .
источник
Простой ответ - просто использовать shared_ptr.
источник
Как по мне,
Просто включите заголовок ...
источник