У меня есть ссылка и хочу вызвать функцию, которая принимает boost :: shared_ptr - PullRequest
3 голосов
/ 25 января 2012

У меня есть ссылка на объект, и я хочу вызвать функцию, которая принимает boost :: shared_ptr этого объекта. Если я создаю boost :: shared_ptr для выполнения вызова, когда мой boost :: shared_ptr отменяется из стека, то объект также отменяется! Это именно то, что происходит, когда я запускаю этот код:

double f(boost::shared_ptr<Obj>& object)
{
  ...
}

double g(Obj& object)
{
  boost::shared_ptr<Obj> p(&object);
  double x = f(p);
  ...
}

Есть ли способ заставить его работать? Как я могу создать в g () указатель boost :: shared, который оставит мой объект живым в конце? Я думаю, что мне нужно подключить его к подсчету ссылок других общих указателей, которые уже указывают на объект ... но как?

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

Ответы [ 6 ]

8 голосов
/ 25 января 2012

Функция, которая принимает shared_ptr, говорит о том, что она делает. Он говорит: «Я хочу претендовать на совместное владение этим объектом». Если это не так, то это плохо написанная функция, которая вообще не должна принимать shared_ptr.

Функция, которая принимает значение, не являющееся const ссылкой на объект, означает, что функция может изменять объект, но не может претендовать на владение. Если у вас нет чего-либо, вы также не можете передать право собственности кому-либо еще.

Теперь вы можете выполнить этот трюк, используя пустую функцию удаления:

void EmptyDeleter(Obj *) {}

double g(Obj& object)
{
  boost::shared_ptr<Obj> p(&object, EmptyDeleter);
  double x = f(p);
  ...
}

Однако, вы сейчас лежите до f. Ему не принадлежит object; он не может владеть object. Вполне возможно, что object является стековым объектом, который может исчезнуть в любое время после завершения f. Если f является членом класса, он может сохранить shared_ptr в переменной-члене. В этот момент у него будет shared_ptr до мертвого объекта . Это именно то, что shared_ptr s предназначены для предотвращения.

Правильный ответ для f, чтобы не принимать аргумент shared_ptr (используйте указатель не const или указатель не const, если он изменяемый, и const&, если он не изменяемый ) или для g, чтобы получить аргумент shared_ptr.

4 голосов
/ 25 января 2012

Вы можете создать shared_ptr, который фактически не освобождает объект.Как это:

struct FictiveDisposer {
    template <class T> void operator ()(T) {}
};

Obj& object = /* ... */;
boost::shared_ptr<Obj> myPtr(&obj, FictiveDisposer ());

// you may use myPtr

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

Простыми словами: вы получили ссылку на объект,Вы не создали его, и вы не можете повлиять на его время жизни (не может shared_ptr).Следовательно, может возникнуть ситуация, когда объект больше не существует, но на него ссылается shared_ptr.Этого следует избегать.

1 голос
/ 25 января 2012

Вы должны рассмотреть цель f ().Предположительно, если он принимает shared_ptr, f намеревается сохранить общее владение этим указателем через некоторое время после его возврата.Зачем?Что предполагает f (), когда вы передаете ему shared_ptr?После того, как вы ответите на этот вопрос, вы сможете выяснить, как кодировать g ().

Если по какой-то причине f () не нужно сохранять общее владение указателем, тогда, если вы контролируетеИнтерфейс может быть переписан для использования Obj * или Obj & вместо shared_ptr.Любой код, имеющий shared_ptr, может затем вызвать f, вытащив указатель из shared_ptr или разыменовав его.

0 голосов
/ 25 января 2012

std::shared_ptr<T> может дать нескольким объектам право собственности на указанный объект.Если интерфейс ожидает std::shared_ptr<T>, есть две возможности:

  1. Кто-то невежественно использовал std::shared_ptr<T> в интерфейсе, который должен был получить объект T или ссылку или указатель на T объект.Если это так, то автор должен получить образование (и, если это не сработает, освободиться от его текущих обязанностей по достижению новой карьеры) и исправить интерфейс.
  2. С тех пор все интерфейсы используют std::shared_ptr<T>используют это для возможного предоставления общего владения объектом, должно быть очевидно, что выделенный стек T не является подходящим аргументом для такого интерфейса.Единственный возможный аргумент - это T объект, время жизни которого поддерживается как std::shared_ptr<T>.

Я понимаю, что это не отвечает на первоначальный вопрос, но должно быть ясно, что единственныйжизнеспособный ход действий: не никогда не пытайтесь передать объект в функцию, получающую std::shared_ptr<T>, которая не может полностью контролироваться таким указателем!(то же самое относится к версии общих указателей boost).

0 голосов
/ 25 января 2012

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

double x = f( boost::shared_ptr<Obj>( new Obj( object ) ) );

Но вам действительно лучше узнать, почему f требует shared_ptr, и почему g не может принять его в качестве аргумента.

0 голосов
/ 25 января 2012

Как я могу создать в g () указатель boost :: shared, который оставит мой объект живым в конце?

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

boost::shared_ptr<Obj> p(&object, [](void*){});

или если ваш компилятор не поддерживает лямбды:

void do_nothing(void*) {}
boost::shared_ptr<Obj> p(&object, do_nothing);

Даже если я заставлю это работать, вы думаете, этот способ плохой дизайн?

Да: вы теряете управление временем жизни, которое дают вам общие указатели, и теперь вы несете ответственность за то, чтобы объект пережил все общие указатели.

Чтоявляется наилучшей практикой для решения подобных проблем?

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

...