Принято отсюда .
Большинство шаблонов в стандартной библиотеке C ++ требуют, чтобы они были созданы с законченными типами. Однако shared_ptrи unique_ptrявляются частичными исключениями. Некоторые, но не все их члены могут быть созданы с неполными типами. Мотивация для этого заключается в поддержке идиом, таких как pimpl, с использованием умных указателей и без риска неопределенного поведения.
Неопределенное поведение может возникнуть, когда у вас есть неполный тип, и вы вызываете deleteего:
class A;
A* a = ...;
delete a;
Выше юридический код. Это скомпилируется. Ваш компилятор может выдавать или не выдавать предупреждение для приведенного выше кода, как указано выше. Когда это выполнится, плохие вещи, вероятно, произойдут. Если вам очень повезет, ваша программа потерпит крах. Однако более вероятным результатом является то, что ваша программа будет молча утекать память, как ~A()не будет вызвано.
Использование auto_ptr<A>в приведенном выше примере не помогает. Вы по-прежнему получаете такое же неопределенное поведение, как если бы вы использовали необработанный указатель.
Тем не менее, использование неполных классов в определенных местах очень полезно! Это где shared_ptrи unique_ptrпомочь. Использование одного из этих умных указателей позволит вам избежать неполного типа, за исключением случаев, когда необходимо иметь полный тип. И самое главное, когда необходимо иметь полный тип, вы получите ошибку во время компиляции, если попытаетесь использовать интеллектуальный указатель с неполным типом в этой точке.
Нет больше неопределенного поведения:
Если ваш код компилируется, то вы использовали полный тип везде, где вам нужно.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptrи unique_ptrтребуют полный тип в разных местах. Причины неясны, связанные с динамическим или статическим удалением. Точные причины не важны. На самом деле, в большинстве кодов вам не очень важно точно знать, где требуется полный тип. Просто код, и если вы ошиблись, компилятор скажет вам.
Тем не менее, в случае, если это полезно для вас, вот таблица, которая документирует несколько членов shared_ptrи в unique_ptrотношении требований к полноте. Если элемент требует полного типа, тогда запись имеет «C», в противном случае запись таблицы заполняется «I».
Complete type requirements for unique_ptr and shared_ptr
unique_ptr shared_ptr
+------------------------+---------------+---------------+
| P() | I | I |
| default constructor | | |
+------------------------+---------------+---------------+
| P(const P&) | N/A | I |
| copy constructor | | |
+------------------------+---------------+---------------+
| P(P&&) | I | I |
| move constructor | | |
+------------------------+---------------+---------------+
| ~P() | C | I |
| destructor | | |
+------------------------+---------------+---------------+
| P(A*) | I | C |
+------------------------+---------------+---------------+
| operator=(const P&) | N/A | I |
| copy assignment | | |
+------------------------+---------------+---------------+
| operator=(P&&) | C | I |
| move assignment | | |
+------------------------+---------------+---------------+
| reset() | C | I |
+------------------------+---------------+---------------+
| reset(A*) | C | C |
+------------------------+---------------+---------------+
Любые операции, требующие преобразования указателей, требуют полных типов для обоих unique_ptrи shared_ptr.
unique_ptr<A>{A*}Конструктор может уйти с неполным Aтолько если компилятор не требуется , чтобы установить вызов ~unique_ptr<A>(). Например, если вы положили в unique_ptrкучу, вы можете уйти с неполным A. Подробнее по этому вопросу можно найти в BarryTheHatchet в ответ здесь .
shared_ptr/unique_ptr» Говарда Хиннанта . Таблица в конце должна ответить на ваш вопрос.