unique_ptr的所有权语义

5

也许我试图过于笼统了。(下面是原始问题) 具体来说,我有一个类Foo的某些依赖Dep。我还有一个类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_TestFoo中声明为MockDep * mock_dep_,而dep_Foo中声明为unique_ptr<Dep> dep_。我该如何让mock_dep_包含dep_的地址呢?(由于std::move(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>,但我更喜欢能够使用后者,因为它明确记录了所有权语义。
最适合(或者说不显眼)解决此问题的方式是什么? 编辑: FooOtherObject 都是我正在定义构造函数的类。

1
OtherObject是什么?据我所知,在那个上下文中它不能是一个函数。 - Shoe
@Jeffrey 我的错,已编辑帖子。 - alecbz
@Jeffrey,我不确定还有什么困惑--我重写了帖子,更直接地涉及到我的问题。 - alecbz
“Dependency” 是什么意思? - M.M
以依赖注入的意义来说,“dependency”指的是类需要使用的某个对象。 - alecbz
2个回答

6

您可以使用以下方法:

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_;
};

第三种解决方案是改变OtherObjectchild_之间的顺序(将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))
    {}
};

1
OtherObject 是根据 OP 的说法一个基类。 - Jarod42
OtherObject 应该是一种任意类型,而不是 unique_ptr - alecbz
使用我编辑后的帖子中的术语,我想避免修改Foo以提供getter(主要是因为它需要static_cast),但另一种解决方案也不太吸引人,所以我可能会选择强制转换访问器。 - alecbz
@alecb:我已经添加了另一种选择,可能更符合你的使用情况。 - Jarod42

1
通常情况下,标准描述如下:
§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。
具体来说,在 Foo 子对象的构造之后,dep 如下:
Foo(std::move(dep))

是一个 nullptr

此外,如果 dep 仍然是一个有效的指针,Foo(std::move(dep)) 将会复制 dep,这对于 std::unique_ptr 的语义来说是没有意义的(因为它是不可复制的)。

你想要做的是让指向对象的引用,在考虑到情况的情况下(例如,unique_ptr 可以是 nullptr 吗?等等),传递给 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)) {}

我相信 unique_ptr 特别保证它被置空了,对吗? - user541686

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接