Создайте boost::shared_ptr для существующей переменной

У меня есть существующая переменная, например.

int a = 3;

Как мне теперь создать от boost::shared_ptr до a? Например:

boost::shared_ptr< int > a_ptr = &a; // this doesn't work

person Bill Cheatham    schedule 11.12.2011    source источник
comment
Зачем тебе это делать?   -  person curiousguy    schedule 13.12.2011
comment
@curiousguy, причина, по которой я это делаю, указана ниже; У меня есть переменная в стеке и есть функция, которой нужна общая точка в качестве входного аргумента.   -  person Bill Cheatham    schedule 16.12.2011
comment
Мне было интересно, зачем функции нужен shared_ptr!   -  person curiousguy    schedule 16.12.2011
comment
Ага, я использую библиотеку облаков точек (pointclouds.org), и весь API предназначен для работы с общие указатели.   -  person Bill Cheatham    schedule 16.12.2011


Ответы (4)


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

int *a=new int;
boost::shared_ptr<int> a_ptr(a);

Тем не менее, вы определенно не хотите помещать переменные стека в shared_ptr. ПЛОХИЕ ВЕЩИ ПРОИЗОЙДУТ

Если по какой-то причине функция принимает shared_ptr, а у вас есть только переменная стека, вам лучше сделать это:

int a=9;
boost::shared_ptr<int> a_ptr=boost::make_shared(a);

Глянь сюда:

http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/make_shared.html

также стоит отметить, что shared_ptr находится в стандарте С++ 11, если вы можете его использовать. Вы можете использовать auto в сочетании с make_shared, как заметки Херба Саттера в разговоре о сборке.

#include <memory>

int a=9;
auto a_ptr=std::make_shared(9);
person 111111    schedule 11.12.2011
comment
Там я объединил два ваших ответа. - person Omnifarious; 12.12.2011
comment
Первое предложение действительно опасно — когда shared_ptr умирает, он удаляет указанный объект, даже если исходный указатель все еще существует. В этой ситуации вы должны использовать средство удаления без операций - см. ответ Люка Дантона. - person Tom Anderson; 19.10.2018

Во-первых, у вас есть ошибка, потому что shared_ptr не будет автоматически конвертироваться из указателя соответствующего типа. Вы должны явно указать, что вы хотите сделать:

int a = 3;
::boost::shared_ptr< int > a_ptr(&a); // DO NOT DO THIS!

Хотя у тебя другая проблема. Представьте себе эффект этого кода:

int a = 3;
delete &a;

В первом примере, который я привел, это неизбежно произойдет, даже если это не так прямолинейно. Вся причина существования shared_ptr заключается в удалении вещей, когда все указатели на него исчезают. Это, конечно, вызовет всевозможные странности в поведении.

У вас есть два пути решения этой проблемы. Один из них — создать что-то, что можно удалить. Другой — убедиться, что shared_ptr на самом деле не удаляет то, на что он указывает. У каждого есть свои плюсы и минусы.

Делаем то, что можно удалить:

Плюсы:

  • Просто и легко.
  • Вам не нужно беспокоиться о времени жизни объекта.

Минусы:

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

Как это сделать:

::boost::shared_ptr<int> a_ptr(::boost::make_shared(a));

Это довольно похоже на (и это также будет работать):

::boost::shared_ptr<int> a_ptr(new int(a));

Но чуть эффективнее. ::boost::make_shared творит чудеса, выделяя счетчик ссылок и объект в непрерывной памяти, что экономит вызовы распределителя и улучшает локальность ссылок.

Делаем так, чтобы shared_ptr фактически не удалял то, на что указывает:

Плюсы:

  • Быстрее, но по-прежнему требует выделения кучи для подсчета ссылок.
  • Непосредственно решает проблему (то, на что вы указываете, не может быть удалено).
  • shared_ptr относится к a, поэтому, если вы измените его значение, все, что обращается к нему через указатель, увидит новое значение.

Минусы:

  • Требуется знать немного больше о том, как работает shared_ptr, а это значит, что люди, читающие ваш код, тоже должны это знать.
  • Если объект, на который вы указываете, выходит из области видимости до того, как это сделают все shared_ptr, указывающие на него, тогда эти указатели станут висящими, и это плохо.
  • Предыдущий пункт делает это очень рискованным решением. Я бы вообще избегал этого.

Как это сделать:

Где-то вне функции (возможно, в анонимном пространстве имен):

void do_nothing_deleter(int *)
{
    return;
}

И затем в функции:

int a = 3;
::boost::shared_ptr a_ptr(&a, do_nothing_deleter);
person Omnifarious    schedule 11.12.2011

То, что вы написали, не будет работать, потому что конструктор shared_ptr, который вы ищете, - это explicit, поэтому вам нужно написать его так

boost::shared_ptr<int> a_ptr(&a); // Don't do that!

Однако проблема с этим заключается в том, что delete будет вызываться для сохраненного значения a_ptr. Поскольку в вашем примере a имеет автоматическую продолжительность хранения, это очень плохо. Таким образом, мы также передаем пользовательский модуль удаления:

boost::shared_ptr<int> a_ptr(&a, noop_deleter);

Реализация noop_deleter для С++ 11:

auto noop_deleter = [](int*) {};

Версия С++ 03:

// Can't be put in local scope
struct {
    void
    operator()(int*) const
    {}
} noop_deleter;
person Luc Danton    schedule 11.12.2011
comment
Это действительно большое знание. Я думаю, что с идеями других людей make_shared легче справиться, но я действительно думаю, что это лучший ответ. - person Omnifarious; 11.12.2011
comment
@Omnifarious: это единственный правильный ответ, поскольку вопрос касается создания shared_ptr для существующей переменной, а не ее копии. Также будет лучше сделать noop_deleter шаблоном. - person Yakov Galka; 11.12.2011
comment
Хотя это интересная информация, я настоятельно рекомендую этого не делать. Сам факт использования shared_ptr заключается в том, что вы не знаете, когда объект будет уничтожен. Однако вы знаете, как долго объект будет находиться в стеке. Поэтому, даже если вы точно знаете, когда он уничтожится, нет смысла делать это, потому что вы всегда можете присвоить значение переменной стека. - person 111111; 11.12.2011
comment
@111111: Есть плюсы и минусы. Огромный риск, которому подвергается этот подход, заключается в том, что shared_ptr не исчезнет раньше, чем переменная стека. Но это НАМНОГО эффективнее, чем создание копии в куче. И ybungalobill прав, это единственный технически правильный ответ на поставленный вопрос. Это не заслужило отрицательного голоса. - person Omnifarious; 11.12.2011
comment
@Omnifarious Хммм, достаточно честно, я, возможно, не должен был голосовать задним числом. Но создание переменной shared_ptr для стека — рискованное дело. Если вы точно знаете, сколько времени требуется переменной, вы, вероятно, не будете использовать shared_ptr. Потому что он знает, как долго он хочет, чтобы он длился (т. Е. Когда он содержит его ответ, он может просто переназначить переменную стека.) - если вы отредактируете ответ, я отзову отрицательный голос BTW. - person 111111; 11.12.2011
comment
Там я объединил два ваших ответа. - person Omnifarious; 12.12.2011
comment
У меня было несколько проблем с синтаксисом, используя ваш пример с linux g++. Мне пришлось поставить имя структуры noop_deleter перед фигурными скобками. Мне пришлось поставить круглые скобки для создания экземпляра noop_deleter при передаче его конструктору shared_ptr. - person Matthew Smith; 03.12.2012
comment
@MattSmith noop_deleter здесь не имя типа, а объект этого (анонимного) типа. - person Luc Danton; 03.12.2012

Вы не можете создать boost::shared_ptr для существующей переменной. Элементы, хранящиеся в boost::shared_ptr, сохраняются при создании.

Однако вы можете создать boost::shared_ptr, который будет копией существующей переменной.

Например

int a = 3; // Existing variable
boost::shared_ptr<int> aCopy = boost::make_shared<int>(a); //Create copy with value of a

Обратите внимание, что вам нужно будет включить <boost/make_shared.hpp> для make_shared.

person Lalaland    schedule 11.12.2011