Как назначить адрес существующего объекта интеллектуальному указателю?

#include <memory>

class bar{};

void foo(bar &object){
    std::unique_ptr<bar> pointer = &object;
}

Я хочу назначить адрес объекта указателю. Вышеприведенный код, очевидно, не будет компилироваться, потому что правая часть оператора присваивания должна быть std:: unique_ptr. Я уже пробовал это:

pointer = std::make_unique<bar>(object)

Но во время компиляции он вызывает много ошибок. Как я могу это сделать?

Обновление
Как сказано в ответах - использование метода std::unique_ptr::reset привело к поведению undefined. Теперь я знаю, что в таких случаях я должен использовать стандартный указатель.

Ответ 1

Попробуйте std:: unique_ptr:: reset

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

Но имейте в виду, что это не рекомендуется, вы не должны создавать unique_ptr для ссылки, передаваемой функции. В конце функции, когда pointer уничтожается, он попытается также уничтожить object, и он не будет доступен вне вызова функции, что приведет к ошибке памяти доступа.

Пример:. Это скомпилирует, но даст ошибку времени выполнения.

struct bar{ int num;};

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

int main()
{
    bar obj;
    foo(obj);
    obj.num; // obj is not a valid reference any more.
    return 0;
}

С другой стороны, вы можете рассмотреть возможность использования shared_ptr, это может помочь вам решить: unique_ptr или shared_ptr?.

Ответ 2

Вы можете вызвать std::unique_ptr::reset:

pointer.reset(&object);

но реальный вопрос: Действительно ли это делает то, что вы ожидаете от него? Если объект не был создан с помощью new, это может быть довольно опасным. Учитывая, что вы не предоставили больше информации, у вас может быть действующий прецедент, но без этой информации он кажется возможным источником будущих проблем. Фактически вы делаете в своем примере то, что вы потребляете объект, то есть после того, как функция была вызвана временем жизни unique_ptr, и объект был удален. Если это задокументировано/ясно для вызывающего, это может быть ОК - иначе переосмыслите дизайн. И даже если это предназначенная конструкция, гораздо лучше использовать unique_ptr в качестве аргумента самой функции:

void foo(std::unique_ptr<bar> pointer)
{
    // ...
}

Это делает две вещи:

  • Сообщает вызывающему, что функция переходит в собственность переданного объекта.
  • Это предотвращает утечку ресурсов.

Ответ 3

Вы можете назначить только unique_ptr или nullptr. Если вы думаете об этом, это тоже имеет смысл (хотя reset позволит вам делать то, что вы хотите, но я думаю, что это на самом деле ошибка или недостаток в unique_ptr).

A unique_ptr является эксклюзивным владельцем объекта с указателем. Когда он выходит из области видимости, он удалит объект.
Это означает, что ваша функция имеет семантику погружения. Указатель, который вы передаете (или, скорее, заостренный объект), потребляется, то есть он "исчезает" (поглощает) внутри функции. Вы передаете объект по ссылке (объект, который даже не обязательно содержит кучу, приготовьтесь к неожиданности, если вы пройдете в объекте с автоматическим хранилищем!) и вдруг он ушел. Bang.

Семантика стока должна быть правильно передана. Вы должны передать параметр unique_ptr как функцию. Уникальные указатели не могут быть скопированы, поэтому это заставит пользователя этой функции использовать std::move, создавая осведомленность о том, что происходит.

Наличие объекта "исчезает" - неприятный сюрприз, это должно происходить не случайно.

Ответ 4

Позвольте мне вас разочаровать, но вы этого не сделаете. У вас есть ссылка, которая может указывать либо на динамически выделенный объект, либо на выделенный стек стек, либо, возможно, на объект в динамически распределенном массиве, который не был выделен индивидуально.

Вы хотите поместить свой адрес в класс, который будет автоматически вызывать удаление по этому адресу. Это недопустимо для большинства ситуаций, представленных выше. Это не правильно. Таким образом, передача ссылки и размещение адреса ссылки в умном указателе никогда не должны выполняться никогда. Особенно не с reset().

В большинстве случаев вам может понадобиться инициализировать интеллектуальный указатель с недавно выделенным объектом, например:

auto ptr = std::unique_ptr<X>(new X(p1, p2));

Никогда не принимайте адрес ссылки. Когда-либо. Без причины. Это морально неправильно. Это не потому, что использование ссылочных адресов является недопустимым, поскольку оно может быть действительным; потому что через два месяца новый коллега захочет использовать этот адрес с помощью умного указателя, потому что, как он это слышал, в настоящее время, а затем перейдите в Qaru и прочитайте, что они должны использовать "reset" для этого указателя Это неправильно, мучительно неправильно.

Ответ 5

Функция, которую вы ищете, reset(). Вот пример:

pointer.reset(&object);

Здесь pointer есть unique_ptr. Вызов reset() уничтожит предыдущий объект, управляемый pointer (если он есть), и сделайте object текущий управляемый объект pointer.

Или, если вы хотите сделать object управляемый объект прав при инициализации unique_ptr:

std::unique_ptr<bar> pointer(&object);

Слово предупреждения: object должно быть выделено new, если вы собираетесь управлять им с помощью unique_ptr, потому что unique_ptr может попытаться позвонить delete на нем. Если он не был создан с помощью new, не переносите его в unique_ptr, это не то, для чего они предназначены.

Ответ 6

Я вижу, что вы хотите иметь объект unique_ptr, который указывает на некоторый объект в памяти, но вы не хотите передавать права собственности на память.

Вам нужно создать пользовательский удалён для объекта, который фактически не удаляет объект:

class bar{};
struct barfakedeleter
{
   void operator()(bar*){/* do nothing here*/}
};

unique_ptr - это шаблон, где второй аргумент является удалением объекта. По умолчанию это default_deleter, но вы можете обменять его на этот поддельный:

void foo(bar& object) 
{
unique_ptr<bar,barfakedeleter> pointer(&object);
....
}

Опубликовать редактирование: То же самое можно сделать с shared_ptr, но поскольку shared_ptr может быть передан другому shared_ptr в дереве наследования объекта, дебетер не сохраняется в аргументе шаблона класса, но он прошел как член экземпляра. Это фактически облегчает использование IMO:

void foo(bar& object)
{
   shared_ptr<bar> pointer(&object,[](bar*){}); // just use anonymous function, which will not delete the object
}

Вы также можете выполнить собственный сбор ресурсов:

shared_ptr<ObjectClass> pointer(GetObject(),[](ObjectClass* obj){ ReleaseTheObject(obj);});