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