shared_ptr сосуществует с указателями - PullRequest
3 голосов
/ 17 декабря 2010

У меня есть функция следующим образом

void a(shared_ptr<X> val) {...}

Иногда я хочу передать объект, выделенный в куче

shared_ptr<X> v(new X());
a(v);

В других случаях я хочу передать в стек-выделенный объект

class C
{
    //doesn't work properly b/c when the shared_ptr dies it will try to delete x...
    C() { a(shared_ptr<X>(&x)); } 
    X x;
};

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

Или я должен пойти по маршруту Java и выделитьвсе из кучи?

Ответы [ 7 ]

9 голосов
/ 17 декабря 2010

Я бы просто заставил функцию принимать параметр шаблона следующим образом:

template<class P>
void func(P ptr) {
     // use ptr like a pointer as usual
     // for example
     *ptr = 10;
}

тогда вы можете сделать так:

shared_ptr<int> v(new int);
int x;

func(v);  // works
func(&x); // also works

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

std::vector<int>::iterator it = v.begin();
func(it); // still works :-)
6 голосов
/ 17 декабря 2010

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

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

5 голосов
/ 17 декабря 2010

Вы можете использовать null_deleter и по-прежнему использовать shared_ptr.

Таким образом, shared_ptr не удалит связанный указатель, когда он будет уничтожен.

struct null_deleter
{
  void operator() (void const*) const {};
};

Вы можете использовать его таким образом:

Foo foo;
shared_ptr<Foo> pfoo(&foo, null_deleter());

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

Рассмотрим следующий случай:

shared_ptr<Foo> pfoo;

{
  Foo foo;
  pfoo.reset(&foo, null_deleter());
} // foo gets destroyed

// Now pfoo points to freed data !
pfoo->doSomething(); // Undefined behavior
3 голосов
/ 25 февраля 2011

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

IКак правило, можно ожидать, что если функция взяла умный указатель, то это потому, что эта функция может захотеть сохранить или манипулировать указателем каким-либо образом.Поскольку ваш пример не передает shared_ptr по ссылке, я могу предположить, что вы не хотите иметь возможность удалять объект изнутри функции.Возможно, вы хотите сохранить общую ссылку для последующего использования, но , которая несовместима с версией вашей функции, которая принимает основанный на стеке объект .Исходя из ваших спецификаций, это могут быть две принципиально разные функции (ЕСЛИ вам действительно нужно взять shared_ptr И вы также хотите принять переменную стека).Итак, как я уже говорил ранее, я предполагаю, что вам на самом деле не нужен shared_ptr внутри функции .

Итак, вот версия вашего кода, которая делает то, что, как мне кажется, вам нужно...

void a( C & val ) // just pass by reference (or const reference)
{
    val.member = 10; // for example
}

shared_ptr<X> v(new X());
a(*v); // simply dereference the pointer to call the function

// verify that v holds an object if it isn't obvious from the context
if ( v ) a(*v);

class C
{
    C() { a(x); } // nothing fancy required to call the function
    X x;
};

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

Еще один пример, полученный из ответа Эвана ...

std::vector<int>::iterator it = v.begin();
a(*it); // also works
3 голосов
/ 17 декабря 2010

Просто заставьте функцию взять обычный (не умный) указатель. Вам не нужно везде использовать умные указатели!

2 голосов
/ 17 декабря 2010

Я думаю, что рискованно пытаться использовать автоматические переменные стека в shared_ptr, поскольку вы по сути избавляетесь от семантики shared_ptr.Даже если вы преодолеете проблемы с удалением, вы все равно останетесь с тем фактом, что у вас есть shared_ptr, который указывает на объект, который на самом деле не хочет «делиться» в этом смысле, а также на владельца и памятьСемантика управления, которая идет с shared_ptr, все идет в окно.В будущем это будет кошмар, если кто-то не поймет, что ты делаешь.

1 голос
/ 17 декабря 2010

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

Как указывает ereOn, вы можете получить что-то, передав нулевое средство удаления, но для пользователейфункция.Моя первая мысль - создать перегрузку для функции, которая берет ссылку и просто переносит версию shared_ptr:

void a(X &val){
  a(shared_ptr<X>(&val, null_deleter()));
}

Это, по крайней мере, избавляет пользователя от грубого взлома.Будет ли это безопаснее, зависит от семантики a().Если вы берете указатель и сохраняете его в каком-то глобальном / постоянном объекте, который будет существовать после возврата a(), вы все равно рискуете неопределенным поведением.В этом случае единственный безопасный способ обойти эту проблему - сделать копию:

void a(const X &val){
  a(shared_ptr<X>(new X(val));
}
...