std::shared_ptr этого
В настоящее время я пытаюсь научиться использовать умные указатели. Однако, проводя некоторые эксперименты, я обнаружил следующую ситуацию, для которой я не смог найти удовлетворительное решение:
Представьте, что у вас есть объект класса 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;
};
Вопрос в том, как объект класса А может пройти std::shared_ptr
само по себе (this
) своему ребенку?
Существуют решения для общих указателей Boost ( Получение boost::shared_ptr
за this
), но как справиться с этим, используя std::
умные указатели?
2 ответа
Есть 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()
из конструктора или деструктора.
У вас есть несколько проблем в дизайне, которые, похоже, связаны с неправильным пониманием умных указателей.
Умные указатели используются для объявления права собственности. Вы нарушаете это, заявляя, что оба родителя владеют всеми детьми, но также и что каждый ребенок является его родителем. Оба не могут быть правдой.
Кроме того, вы возвращаете слабый указатель в getChild()
, Делая так, вы заявляете, что абонент не должен заботиться о владельце. Теперь это может быть очень ограничивающим, но при этом вы должны убедиться, что рассматриваемый дочерний элемент не будет уничтожен, пока все еще удерживаются слабые указатели, если вы используете умный указатель, он будет отсортирован сам по себе,
И последнее. Обычно, когда вы принимаете новые объекты, вы обычно должны принимать необработанные указатели. Умный указатель может иметь собственное значение для обмена детьми между родителями, но для общего использования вы должны принимать необработанные указатели.