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