std :: shared_ptr цього


101

Зараз я намагаюся навчитися користуватися розумними вказівниками. Однак, проводячи деякі експерименти, я виявив наступну ситуацію, для якої не міг знайти насичуючого рішення:

Уявіть, у вас є об'єкт класу A, який є батьком об'єкта класу B (дочірній), але обидва повинні знати один одного:

class A;
class B;

class A
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children->push_back(child);

        // How to do pass the pointer correctly?
        // child->setParent(this);  // wrong
        //                  ^^^^
    }

private:        
    std::list<std::shared_ptr<B>> children;
};

class B
{
public:
    setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    };

private:
    std::shared_ptr<A> parent;
};

Питання в тому, як об’єкт класу A може передати std::shared_ptraself ( this) своїй дочірній організації?

Є рішення для Boost спільних покажчиків ( Отримання boost::shared_ptrforthis ), але як з цим впоратися за допомогою std::розумних покажчиків?


2
Як і будь-яким іншим інструментом, вам доведеться використовувати його, коли це доречно. Використовувати розумні покажчики для того, що ви робите, це не
YePhIcK

Подібно до підсилення. Дивіться тут .
juanchopanza

1
Це проблема на такому рівні абстракції. Ви навіть не знаєте, що "це" вказує на пам'ять у купі.
Vaughn Cato

Ну, мова ні, але ти робиш. Поки ви відстежуєте, що де, у вас все буде добре.
Алекс

Відповіді:


168

Є std::enable_shared_from_thisсаме для цього. Ви успадковуєте від нього і можете дзвонити .shared_from_this()зсередини класу. Крім того, ви створюєте тут кругові залежності, які можуть призвести до витоків ресурсів. Це можна вирішити за допомогою std::weak_ptr. Тож ваш код може виглядати приблизно так (припускаючи, що діти покладаються на існування батьківського, а не навпаки):

class A;
class B;

class A
    : public std::enable_shared_from_this<A>
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children.push_back(child);

        // like this
        child->setParent(shared_from_this());  // ok
        //               ^^^^^^^^^^^^^^^^^^
    }

private:     
    // note weak_ptr   
    std::list<std::weak_ptr<B>> children;
    //             ^^^^^^^^
};

class B
{
public:
    void setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    }

private:
    std::shared_ptr<A> parent;
};

Однак зверніть увагу, що для здійснення дзвінків .shared_from_this()потрібно мати право thisвласності std::shared_ptrна момент дзвінка. Це означає, що ви більше не можете створювати такий об’єкт у стеці і, як правило, не можете викликати .shared_from_this()всередині конструктора чи деструктора.


1
Дякую за пояснення та за вказівку на мою проблему кругової залежності.
Ікар

@Deduplicator що ти маєш на увазі?
yuri kilochek

Спробуйте побудувати shared_ptrбазу на основі побудованої за замовчуванням shared_ptrі на все, що ви хочете вказати на неї ...
Дедулікатор

1
@Deduplicator - це, пробачте, каламбур, досить безглуздий спільний вказівник. Цей конструктор призначений для використання з вказівниками на члени керованого об'єкта або його бази. У будь-якому випадку, у чому ваша думка (вибачте)? Ці неволодіючі питання shared_ptrне мають значення для цього питання. shared_from_thisУ передумовах Росії чітко зазначено, що об'єкт повинен належати (а не просто вказувати) деяким shared_ptrу пункті виклику.
yuri kilochek

1
@kazarey Право власності на a shared_ptrвимагається в момент виклику, але за типовим шаблоном використання, тобто щось на зразок shared_ptr<Foo> p(new Foo());, shared_ptrнабуває права власності на об’єкт лише після його повної побудови. Це можна обійти, створивши shared_ptrконструктор, ініціалізований за допомогою, thisта зберігаючи його десь нелокально (наприклад, у аргументі посилання), щоб він не вмер, коли конструктор завершиться. Але цей заплутаний сценарій навряд чи буде необхідним.
yuri kilochek

9

У вас є кілька проблем у дизайні, які, здається, походять від вашого нерозуміння розумних покажчиків.

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

Крім того, ви повертаєте слабкий вказівник в getChild(). Цим ви заявляєте, що абонент не повинен дбати про право власності. Зараз це може бути дуже обмежуючим, але, роблячи це, ви повинні переконатися, що дитина, про яку йдеться, не буде знищена, поки будуть утримуватися будь-які слабкі покажчики; якщо ви використовуєте розумний вказівник, він буде розібраний сам по собі .

І остання річ. Зазвичай, приймаючи нові сутності, зазвичай слід приймати необроблені вказівники. Розумний вказівник може мати своє власне значення для обміну дітьми між батьками, але для загального користування слід приймати необроблені вказівники.


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