Возможно, я пытался быть слишком общим. (Оригинальный вопрос ниже) Конкретно, у меня есть некоторая зависимость Dep
класса Foo
, У меня тоже есть класс MockDep
и я определяю класс TestFoo
, Вот его конструктор, который я попытался написать:
TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)), mock_dep_(dep.get()) {}
А также Foo
Конструктор выглядит так:
Foo(unique_ptr<Dep> dep) : dep_(dep) {}
mock_dep_
Delcared в TestFoo
как MockDep* mock_dep_
, а также dep_
объявлен в Foo
как unique_ptr<Dep> dep_
, Как я могу получить mock_dep_
содержать dep_
адрес? (так как выше не работает, так как std::move(dep)
обнуляет dep
.)
Исходное сообщение:
У меня есть объект типа Foo
что я должен передать другому объекту типа OtherObject
который претендует на владение им, но как указатель на его базовый класс. Однако я хочу получить указатель на дочерний объект, который я могу использовать для ссылки на него. Я написал что-то вроде:
Foo(std::unique_ptr<Child> thing) :
OtherObject(std::move(thing)), child_(thing.get()) {}
OtherObject(std::unique_ptr<Base> thing, ...) { ... }
Тем не менее, это не похоже на работу, так как std::move(thing)
кажется, обнулить указатель, который возвращается из thing.get()
потом.
я могу изменить Foo
параметр, чтобы иметь тип Child*
вместо unique_ptr<Child>
, но я предпочел бы иметь возможность сделать последнее, поскольку оно явно документирует семантику владения.
Каков наиболее подходящий (или неудачный, ненавязчивый) способ решения этой проблемы?
редактировать: Foo
а также OtherObject
оба должны быть классами, чьи конструкторы я определяю.
Вы можете использовать:
Foo(std::unique_ptr<Child> thing) :
OtherObject(std::move(thing)),
child_(OtherObject.getChildPtr()) /* one accessor to get the pointer. */
{}
Если базовый объект OtherObject
не предоставляет средства доступа к указателю, вы можете делегировать конструктор другому конструктору, например:
class Foo: public OtherObject
{
public:
Foo(std::unique_ptr<Child> thing) : Foo(thing, thing.get()) {}
private:
Foo(std::unique_ptr<Child>& thing, Child* child) :
OtherObject(std::move(thing)),
child_(child)
{}
private:
Child* child_;
};
Третьим решением было бы изменить порядок между OtherObject
а также child_
(иметь child_
до) путем введения другого деривации:
class ChildPtr
{
public:
ChildPtr(Child* child) : child_(child) {}
Child* child_;
};
class Foo: private ChildPtr, public OtherObject
{
public:
Foo(std::unique_ptr<Child> thing) :
ChildPtr(thing.get()),
OtherObject(std::move(thing))
{}
};
Обычно то, что происходит, описано в стандарте как:
§17.6.5.15.1 Состояние перемещенных из библиотеки типов [Lib.types.movedfrom]
Объекты типов, определенных в стандартной библиотеке C ++, могут быть перемещены из (12.8). Операции перемещения могут быть явно указаны или неявно сгенерированы. Если не указано иное, такие перемещенные объекты должны быть помещены в допустимое, но неопределенное состояние.
Стандарт на самом деле конкретно описывает поведение std::unique_ptr
в:
§20.8.1.4 Шаблон класса unique_ptr [Unique.ptr]
Дополнительно,
u
может, по запросу, передать право собственности другому уникальному указателюu2
, После завершения такого перевода выполняются следующие постусловия:
- u2.p равна предварительной передаче u.p,
- u.p невероятно похож на nullptr, а
- если состояние предварительной передачи u.d поддерживалось, такое состояние было передано в u2.d.
В частности, dep
после строительства Foo
подобъект в:
Foo(std::move(dep))
это nullptr
,
Более того, если dep
был еще действительным указателем, Foo(std::move(dep))
скопировал бы dep
что не имеет смысла для std::unique_ptr
семантическая (как не копируемая).
То, что вы хотите сделать, это позволить ссылку на указанный объект с учетом обстоятельств дела (например, может ли unique_ptr быть nullpt
? и т. д.), в Foo
:
class Foo {
public:
Foo(unique_ptr<Dep> dep) : dep_(dep) {}
const Dep& get_dep() const { return *dep_; }
Dep& get_dep() { return *dep_; }
private:
std::unique_ptr<Dep> dep_;
};
а затем просто построить TestFoo
объект как:
TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)) {}