У меня есть несколько вопросов, связанных с использованием shared_ptr, указывающего на базовый класс.Их ответы влияют друг на друга, и для всех трех мне нужен один и тот же фрагмент кода, чтобы установить минимально возможный контекст, например this (все вопросы относятся к Foo
и его held_object_
):
#include <memory>
#include <utility>
class Bar // interface for Scene (see linked question)
{
public:
virtual ~Bar() = 0;
// more virtual methods to work with Scene
};
Bar::~Bar() = default;
class Baz : public Bar // interface for Foo
{
public:
virtual ~Baz() = 0;
// more virtual methods to work with Foo
};
Baz::~Baz() = default;
class ConcreteBaz : public Baz // actual thing I'm acquiring and releasing
{
// overrides and whatnot
};
class Foo
{
public:
void acquire(const std::shared_ptr<Baz>& object) {};
void release(/* actually takes a place to release into */) {};
private:
std::shared_ptr<Baz> held_object_;
};
int main()
{
auto foo = std::make_unique<Foo>();
auto p_foo = foo.get();
while (/* condition */)
{
auto cbaz = std::make_shared<ConcreteBaz>();
// Scene gets a copy here
p_foo->acquire(cbaz);
// do physical things to cbaz
p_foo->release(/* actually takes a place to release into */);
// do other physical things, then acquire cbaz again
p_foo->acquire(cbaz);
// do physical things to cbaz
p_foo->release(/* actually takes a place to release into */);
}
}
Как видите, cbaz
- указатель на полиморфную иерархию, которая может работать как с Scene
, так и с Foo
.ConcreteBaz
на самом деле представляет собой физический объект , которым Foo
может, как указано в коде, стать владельцем (это shared_ptr
, поскольку Scene
и Foo
владеют им, согласно это . Я удалил здесь детали Scene
, потому что это OT).
Вопросы
1) Как инициализировать held_object_
?Могу ли я сделать это с одним выделением?
В действительности, foo
не владеет cbaz
, пока не получит его (физически), поэтому конструктор должен инициализировать указатель на nullptr.Первоначально я хотел использовать make_shared
согласно this , но я прочитал здесь , что он инициализирует значение, то есть, если бы я должен был
Foo::Foo()
: held_object_ {std::make_shared<Baz>()} // equivalent to std::make_shared<Baz>(nullptr)
{
}
компиляторбудет жаловаться с error: invalid new-expression of abstract class type
.Этот будет , таким образом, станет дубликатом этого вопроса, но принятый ответ либо оставит shared_ptr
неинициализированным, либо создаст еще unique_ptr
для ... в принципе, неясно, как я буду применятьэто здесь, и я не думаю, что я могу вызвать reset
в конструкторе (?).
Должен ли я вместо этого быть явным и сказать
Foo::Foo()
: held_object_ {std::shared_ptr<Baz> {nullptr}}
{
}
, не заботясь о двойном распределениикоторый make_shared
избегает?В этот момент не будет ли он почти точно идентичным : held_object_ {}
(отодвиньте ctor от ctor по умолчанию в сторону)?РЕДАКТИРОВАТЬ: Могу ли я сделать это с помощью одного распределения, как make_shared
делает ?
2) Как мне управлять held_object_
?
Прямо сейчас, чтобы получить право собственности на cbaz
после вызова acquire
Я в конечном итоге получаю вызов для метода
void Foo::setHeldObject_(std::shared_ptr<Baz> object)
{
this->held_object_ = std::move(object);
}
, однако в release
мне придется уничтожить владельца shared_ptr
(чтобы затем установить его снова (следующий цикл) и сделать состояние моего экземпляра Foo
согласованным с его физическим состоянием IRL.Это заставило меня задуматься об использовании std :: shared_ptr :: reset (прежде чем даже прочитать предыдущий связанный ответ), потому что я бы заменил pointee на nullptr, если бы я вызвал setHeldObject()
и установил cbaz
в противном случае.Однако я не могу определить правильный синтаксис для тела метода, так как:
this->held_object_.reset(object);
явно неверен, так как я хочу управлять указателем, а не указателем (и поэтому не компилируется); this->held_object_.reset(&object);
выглядит неправильно и возвращает error: cannot convert ‘std::shared_ptr<Baz>*’ to ‘Baz*’ in initialization
this->held_object_.reset(object.get());
также кажется неправильным, потому что я не думаю, что я поднимаю user_count
таким образом (он компилируетсяхотя).
РЕДАКТИРОВАТЬ: Я считаю, что это должно быть возможно сделать с тем же вызовом метода, давая или не давая аргумент.Возможно ли это с reset
?
3) На самом деле есть только два владельца
Функция main
, которую я здесь написал, на самом деле является классом Process
'run
метод, но его определение таким образом делает так, чтобы use_count увеличивался до 3 к моменту времени Scene
и Foo
, имеющих свои shared_ptr
.Начиная с I make_shared
и далее, я нахожусь в цикле, более того, логика говорит, что должно быть только два владельца.Это вариант использования для weak_ptr
, где может иметь смысл сделать что-то вроде:
int main()
{
auto foo = std::make_unique<Foo>();
auto p_foo = foo.get();
while (/* condition */)
{
auto cbaz = std::make_shared<ConcreteBaz>();
auto p_cbaz = std::weak_ptr<ConcreteBaz> {cbaz};
// Scene gets a && via std::move here
p_foo->acquire(p_cbaz.lock()); // this instantly gets converted to a shared_ptr EDIT: BUT IT DOESN'T INCREASE THE USE COUNT
// do physical things to cbaz
p_foo->release(/* actually takes a place to release into */);
// do other physical things, then acquire cbaz again
p_foo->acquire(p_cbaz.lock()); // this instantly gets converted to a shared_ptr and would be nullptr had I moved in the first acquire call EDIT: BUT IT DOESN'T INCREASE THE USE COUNT
// do physical things to cbaz
p_foo->release(/* actually takes a place to release into */);
}
}
или есть лучший способ?