Отримання boost :: shared_ptr для цього


76

Я широко використовую boost:shared_ptrсвій код. Насправді більшість об'єктів, що виділяються в купі, утримуються a shared_ptr. На жаль, це означає, що я не можу перейти thisдо жодної функції, яка займає a shared_ptr. Розглянемо цей код:

void bar(boost::shared_ptr<Foo> pFoo)
{
    ...
}

void Foo::someFunction()
{
    bar(this);
}

Тут є дві проблеми. По-перше, це не компілюється, оскільки конструктор T * для shared_ptrявний. По-друге, якщо я змушую його будувати за допомогою, bar(boost::shared_ptr<Foo>(this))я створив другий спільний вказівник на мій об’єкт, що врешті-решт призведе до подвійного видалення.

Це підводить мене до мого запитання: чи існує якийсь стандартний шаблон для отримання копії існуючого спільного вказівника, який, як ви знаєте, існує зсередини методу на одному з цих об’єктів? Чи є використання нав'язливого підрахунку посилань моїм єдиним варіантом тут?


« Чи є використання нав'язливих посилань для підрахунку мого єдиного варіанту тут? » Що поганого в цьому варіанті?
curiousguy

Може, нічого. Залежить від ваших обставин. Це робить ваші об’єкти більшими і може не працювати в місцях, де у вас немає контролю над класами, до яких ви тримаєте розумні вказівники.
Джо Людвіг,

enabe_shared_from_this зараз у std::. Погляньте на мою відповідь.
Йоган Лундберг

Відповіді:


102

Ви можете отримати з enable_shared_from_this, а потім ви можете використовувати "shared_from_this ()" замість "this", щоб створити спільний вказівник на власний власний об'єкт.

Приклад за посиланням:

#include <boost/enable_shared_from_this.hpp>

class Y: public boost::enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

Це хороша ідея при появі потоків із функції-члена для підвищення bonding :: bind до shared_from_this () замість цього. Це гарантуватиме, що об’єкт не буде звільнений.


f () схожий на ".Copy ()", а також неглибока копія.
Антон Андрєєв

19

Просто використовуйте сирий вказівник для параметра функції замість shared_ptr. Призначення розумного вказівника - керувати часом життя об’єкта, але термін служби об’єкта вже гарантується правилами масштабування C ++: він буде існувати принаймні стільки, скільки закінчується ваша функція. Тобто викличний код не може видалити об’єкт до того, як повернеться ваша функція; таким чином, безпека "німого" вказівника гарантована, якщо ви не намагаєтесь видалити об'єкт у своїй функції.

Єдиний раз, коли вам потрібно передати shared_ptr у функцію, це коли ви хочете передати право власності на об’єкт функції або хочете, щоб функція зробила копію вказівника.


1
Домовились. Багато разів ви можете використовувати foo (const Object * object_ptr) {} foo (obj.get ()); де obj - boost :: shared_ptr <Object>. Шукайте в Інтернеті Херб Саттер, іншого автора статті, який має чудову інформацію про цю та подібні проблеми.
млрд.

1
Це трохи поруч із суттю, але ... Якщо ви можете використовувати покажчик, то (швидше за все) ви можете використовувати посилання, що краще IMO.
denis-bu

1
@ denis-bu, за винятком випадків, коли можливий NULLвказівник. Але ви робите добру думку.
Марк Ренсом

Очевидно, що на це запізнився 12 років, але я не розумію цієї відповіді. Тип аргументу bar відрізняється від вихідного вказівника. Без ключового слова "явного", яке призведе до невдалої компіляції передачі "цього", компілятор не спробує побудувати shared_ptr із необробленого вказівника, створюючи в процесі блок підрахунку посилань, встановіть кількість посилань на 1, а потім на вийти з панелі, зменшити кількість посилань на 0 і спробувати видалити "це"?
Фостер Бундогг

1
@FosterBoondoggle моя пропозиція полягала в тому, щоб змінити тип параметра barна необроблений вказівник.
Марк Ренсом,


9

Ви дійсно робите більше спільних копій pFoo всередині панелі? Якщо ви не робите нічого божевільного всередині, просто зробіть це:


void bar(Foo &foo)
{
    // ...
}

5

З C ++ 11 shared_ptrі enable_shared_from_thisзараз знаходиться у стандартній бібліотеці. Останнє, як випливає з назви, саме для цього випадку.

http://en.cppreference.com/w/cpp/memory/shared_ptr

http://en.cppreference.com/w/cpp/memory/enable_shared_from_this

Приклади на основі цього у посиланнях вище:

struct Good: std::enable_shared_from_this<Good>{
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
};

використання:

std::shared_ptr<Good> gp1(new Good);
std::shared_ptr<Good> gp2 = gp1->getptr();
std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';

3

Функція, яка приймає покажчик, хоче виконати одну з двох поведінок:

  • Власний об’єкт, що передається, і видаляти його, коли він виходить за межі обсягу. У цьому випадку ви можете просто прийняти X * і негайно обернути scoped_ptr навколо цього об'єкта (у тілі функції). Це буде працювати, щоб прийняти "це" або, загалом, будь-який об'єкт, виділений куче.
  • Поділіться покажчиком (не володійте ним) на переданий об'єкт. У цьому випадку ви взагалі не хочете використовувати scoped_ptr, оскільки ви не хочете видаляти об'єкт в кінці вашої функції. У цьому випадку те, що вам теоретично потрібно, це shared_ptr (я бачив, як це називається linked_ptr в іншому місці). Бібліотека підсилення має версію shared_ptr , і це також рекомендується в книзі Скотта Майерса "Ефективний C ++" (пункт 18 у 3-му виданні).

Редагувати: На жаль, я трохи неправильно прочитав питання, і тепер я бачу, що ця відповідь не стосується цього питання. Я все одно залишу це, на випадок, якщо це може бути корисним для тих, хто працює над подібним кодом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.