【问题标题】:Using virtual objects with shared_ptr/unique_ptr in a container class在容器类中使用带有 shared_ptr/unique_ptr 的虚拟对象
【发布时间】:2017-12-05 18:25:54
【问题描述】:

我对 C++(来自 Java/PHP)相当陌生。基本上,我需要创建一个容器类,它将保存对虚拟类实例(基类)的唯一/共享指针的引用。我无法编译这段代码(我使用的是 MSVC 2015)。

编译错误是:

error C2280: 'std::unique_ptr<Base,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function

这是一个重现问题的示例应用程序:

#include <iostream>
#include <memory>

class Base {
    public:
        virtual void foo() const = 0;
        virtual void bar() const = 0;
};

class Derived : public Base {
    public:
        void foo() const override {
            std::cout << "foo" << std::endl;

        };

        void bar() const override{
            std::cout << "bar" << std::endl;
        };
};

class ContainerUnique {
    public:
        ContainerUnique() {
            ptr = nullptr;
        }

        void assignPtr(const Base &instance) {
            auto ptr = std::make_unique<Base>(instance);
            ptr.swap(ptr);
        };

        std::unique_ptr<Base> getPtr() {
            return ptr;
        };

    private:
        std::unique_ptr<Base> ptr;
};

class ContainerShared {
    public:
        ContainerShared() {
            ptr = nullptr;
        }

        void assignPtr(const Base &instance) {
            auto ptr = std::make_shared<Base>(instance);
            ptr.swap(ptr);
        };

        std::shared_ptr<Base> getPtr() {
            return ptr;
        };

    private:
        std::shared_ptr<Base> ptr;
};

int main() {
    Derived derived1 = Derived();
    Derived derived2 = Derived();

    ContainerUnique cu = ContainerUnique();
    ContainerShared cs = ContainerShared();

    cu.assignPtr(derived1);
    cs.assignPtr(derived2);

    std::unique_ptr<Base> uptr = cu.getPtr();
    std::shared_ptr<Base> sptr = cs.getPtr();

    return 0;
}

请指教

【问题讨论】:

  • std::unique_ptr&lt;Base&gt; getPtr() 返回一个副本。你不能复制unique_ptr。我不确定您要达到什么语义。 getPtr 是否应该共享所有权?如果是这样,您不能使用unique_ptr。如果不是,请不要返回智能指针。
  • 与错误无关:在这种情况下,您不需要覆盖的默认构造函数,也不应该使用ptr.swap(或同名的临时变量)。只需ptr = std::make_unique&lt;Base&gt;(instance); 就足够了
  • @UnholySheep。我刚试了一下,编译器抛出error C2259: 'Base': cannot instantiate abstract class
  • assignPtr() 接收到const Base&amp; 引用并尝试复制构造一个新的Base 对象,而不是Derived,因此出现错误。此外,assignPtr() 将新的std::unique_ptr 分配给局部变量ptr,然后将swap 分配给它本身,而不是类的ptr 成员。您需要更改ContainerUnique::assignPtr() 以将std::unique_ptr&lt;Base&gt; 作为输入并将std::move() 更改为类的ptr 成员,并更改ContainerShared::assignPtr() 以将std::shared_ptr&lt;Base&gt; 作为输入并将其分配给类的ptr成员。
  • @RemyLebeau:但是您会尝试(并且失败)获得具有自动生命周期的变量的unique_ptr 所有权。整个设计是一个 X-Y 问题,有很多破损,有很多不同的方向要让它合法,没有任何描述预期用途的东西,所以没有办法在它们之间做出选择。

标签: c++ c++14 virtual shared-ptr unique-ptr


【解决方案1】:

您应该考虑对象所有权以及对象所在的位置(堆栈或堆),在 Java 中您可能不必关注这些。

因为ContainerUnique 有一个unique_ptr 成员,我推断它打算拥有指向的对象。它被赋予删除它的义务。这和其余的不一致,因为derived1在栈上,所以不需要被任何东西拥有,也不能被删除。相反,假设我们在堆上创建了derived1

auto derived1 = std::make_unique<Derived>();

这会在堆上创建它,因此它需要管理,并通过将其指针存储在unique_ptr 中来标识我们拥有该对象。要使用unique_ptr 转移所有权,我们必须调整界面:

// Interface
void ContainerUnique::assignPtr(std::unique_ptr<Base> instance);
// Call site
cu.assignPtr(std::move(derived1));

如果我们采用原始引用或指针,则接口或调用站点中没有任何内容可以反映所有权的预期更改。如果我们使用对 Base 的引用并调用 std::make_unique,我们正在制作一个副本,并制作一个 Base 的副本(丢失任何派生数据)。使用unique_ptr 可能是传达所有权转让的最清晰方式。

把它放在一起:

#include <memory>

struct Base {
    virtual void foo() const = 0;
    virtual void bar() const = 0;
};

struct Derived : Base {
    void foo() const override { }
    void bar() const override { }
};

class ContainerUnique {
    public:
        void assignPtr(std::unique_ptr<Base> instance) {
            ptr.swap(instance);
        };

        std::unique_ptr<Base> getPtr() {
            return std::move(ptr);
        };

    private:
        std::unique_ptr<Base> ptr;
};

int main() {
    auto derived1 = std::make_unique<Derived>();
    auto cu = ContainerUnique();
    cu.assignPtr(std::move(derived1));
    auto uptr = cu.getPtr();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 2013-12-09
    • 1970-01-01
    • 2014-01-11
    • 1970-01-01
    • 2017-05-19
    相关资源
    最近更新 更多