Почему умные указатели нельзя объявлять обычным способом Pointer

Я прочитал документацию об инициализации уникального указателя здесь . Я попытался объявить уникальный указатель таким же образом (see unique_ptr<int> temp1 {&h}, я не видел этого типа объявления в документах, просто экспериментировал), я объявляю неумный указатель. Идея проведения этого эксперимента заключалась в том, чтобы увидеть, как работает метод std::unique_ptr::get(). Вот код:

#include<iostream>
#include<stdio.h>
#include<memory>

using namespace std  ;
int main(){

int h {100};
unique_ptr<int> temp1 {&h};

cout<<"temp1.get() :"<<temp1.get()<<endl;
cout<< "&h : "<<&h<<endl;
cout<<"*temp : "<<*temp1<<endl;
    return 0 ; 
}

Код компилируется, и я получаю следующий вывод:

temp1.get() :0x7ffd4322c5cc
&h : 0x7ffd4322c5cc
*temp : 100
/home/abhishek/.codelite/tmp/abhishek/codelite-exec.sh: line 3:  7889 Segmentation fault      (core dumped) ${command}
Hit any key to continue...

Я вижу, что std::unique_ptr::get() возвращает адрес управляемого объекта, который совпадает с &h. О чем здесь говорит ошибка? Хотя назначение адреса интеллектуальному указателю обсуждалось здесь . Это не отвечает на мой вопрос.


person warrior_monk    schedule 29.12.2019    source источник
comment
unique_ptr пытается delete сделать что-то, не выделенное new.   -  person KamilCuk    schedule 29.12.2019
comment
Присвоение адреса интеллектуальному указателю обсуждалось здесь. Это не ответ на мой вопрос. Да, это так. В вопросе говорится: «Теперь я знаю, что в таких случаях я должен использовать стандартный указатель». Это твой ответ.   -  person Nicol Bolas    schedule 29.12.2019
comment
@NicolBolas, я склонен не согласиться. Во-первых, аналогичный код в этом вопросе не компилируется.   -  person warrior_monk    schedule 29.12.2019
comment
@warrior_monk: Компилируется ли код, не имеет особого значения. Суть в том, что вы оба пытаетесь сделать одно и то же: использовать интеллектуальный указатель для управления временем жизни объекта, время жизни которого является автоматическим и уже будет обрабатываться компилятором.   -  person Nicol Bolas    schedule 29.12.2019
comment
@NicolBolas . Спасибо, теперь я понимаю, о чем вы говорите. Новичок в C++, доберусь туда!   -  person warrior_monk    schedule 29.12.2019


Ответы (3)


Как указано здесь:

Объект удаляется с использованием потенциально предоставляемого пользователем средства удаления путем вызова get_deleter()(ptr). Средство удаления по умолчанию использует оператор удаления, который уничтожает объект и освобождает память.

unique_ptr содержит указатель на динамически выделяемые переменные, хранящиеся в куче. Когда вы инициализировали int h, вы сохранили эту переменную в стеке. Совершенно очевидно, что вы не должны использовать удаление для чего-либо, не выделенного динамически с использованием нового, поэтому вместо этого вам придется сделать следующее:

int* h_ptr = new int (100);
unique_ptr<int> temp1 {h_ptr};
person NaShBe    schedule 29.12.2019

Как указано в одном из ответов на вопрос, на который вы ссылаетесь:

unique_ptr является эксклюзивным владельцем указанного объекта. Когда он выйдет за рамки, он удалит объект.

То есть указатель, который вы передаете std::unique_ptr, должен полностью принадлежать результирующему std::unique_ptr. Это связано с тем, что как только std::unique_ptr получит указатель, он будет управлять указателем и попытается delete его, как только std::unique_ptr выйдет из области видимости.

Однако о полной собственности здесь не может быть и речи. h — локальная переменная. Он будет уничтожен, как только h выйдет из области видимости. Но он также будет уничтожен, как только temp1 выйдет из области видимости. Итак, две вещи попытаются его разрушить. Это почти наверняка приведет к неопределенному поведению, которое, вероятно, вы видите здесь.

Вот почему вы не должны передавать адреса локальных переменных в std::unique_ptr, а лучше передавать адреса, которые выделяются динамически, например, через new или std::make_unique().

person Community    schedule 29.12.2019

Почему умные указатели нельзя объявлять обычным способом Pointer

std::shared_ptr/std::uniqure_ptr неявно владеют объектом, поэтому они несут ответственность за удаление этого объекта, и вам не разрешено удалять объект с помощью delete, владельцем которого является интеллектуальный указатель. std::experimental::observer_ptr (TS v2) и std::weak_ptr не являются собственниками.

Необработанный указатель на другой стороне может быть как владельцем, так и не владельцем, поэтому вам необходимо задокументировать, владеет ли этот указатель и отвечает ли тот, у кого есть этот указатель, вызывать delete для него или нет.

Итак, то, что вы делаете с unique_ptr<int> temp1 {&h};, такое же, как и с необработанным указателем, для которого вы задокументировали, что он является необработанным указателем-владельцем, и для которого вы вызываете delete.

int h {100};
int * temp1 = &h; // none owning pointer

// … some code here …

delete temp1; // calling delete on temp1 is not valid and would result in the same problem as with your unique_ptr example.

И возможность передачи права собственности на объект зависит от длительности хранения.

h попадает в группу автоматически:

автоматическая продолжительность хранения. Память для объекта выделяется в начале окружающего блока кода и освобождается в конце. Все локальные объекты имеют этот срок хранения, кроме объявленных static, extern или thread_local.

Кроме того, право собственности не может быть передано, поскольку оно принадлежит исключительно включающему блоку кода.

Право собственности может быть передано только для объектов с динамическим сроком хранения:

динамическая продолжительность хранения. Хранилище для объекта выделяется и освобождается по запросу с помощью функций динамического выделения памяти. Подробнее об инициализации объектов с такой продолжительностью хранения см. в new-expression.

(автоматически – это то, что часто называют выделенным в стеке, а динамическое – это то, что часто называют выделенным в куче.)

person t.niese    schedule 29.12.2019