Принято отсюда .
Большинство шаблонов в стандартной библиотеке 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
» Говарда Хиннанта . Таблица в конце должна ответить на ваш вопрос.