c++ - 如何使用智能指针作为类属性来复制对象?

标签 c++ smart-pointers copy-constructor assignment-operator

来自boost library documentation我读了这个:

Conceptually, smart pointers are seen as owning the object pointed to, and thus responsible for deletion of the object when it is no longer needed.

我有一个非常简单的问题:我想将 RAII 用于可复制和可赋值类的指针属性。

复制和赋值操作应该很深:每个对象都应该有自己的实际数据拷贝。此外,RTTI 需要可用于属性(它们的类型也可以在运行时确定)。

我应该搜索 Copyable 智能指针的实现(数据很小,所以我不需要 Copy on Write 指针),还是将复制操作委托(delegate)给对象的复制构造函数,如下所示this answer

对于可复制和可赋值的类的简单 RAII,我应该选择哪个智能指针? (我认为将复制/赋值操作委托(delegate)给类复制构造函数和赋值运算符的 unique_ptr 会做出正确的选择,但我不确定)

这是使用原始指针的问题的伪代码,它只是一个问题描述,而不是正在运行的 C++ 代码:

// Operation interface
class ModelOperation
{
    public: 
        virtual void operate = (); 
};

// Implementation of an operation called Special 
class SpecialModelOperation
:
    public ModelOperation
{
    private:
        // Private attributes are present here in a real implementation. 

    public: 

        // Implement operation
        void operate () {}; 
};

// All operations conform to ModelOperation interface
// These are possible operation names: 
// class MoreSpecialOperation; 
// class DifferentOperation; 

// Concrete model with different operations
class MyModel 
{
    private: 
        ModelOperation* firstOperation_; 
        ModelOperation* secondOperation_;  

    public:

        MyModel()
            : 
                firstOperation_(0), 
                secondOperation_(0)
        {
            // Forgetting about run-time type definition from input files here.
            firstOperation_  = new MoreSpecialOperation(); 
            secondOperation_ = new DifferentOperation(); 
        }

        void operate()
        {
            firstOperation_->operate(); 
            secondOperation_->operate();
        }

        ~MyModel() 
        {
            delete firstOperation_; 
            firstOperation_ = 0; 

            delete secondOperation_; 
            secondOperation_ = 0; 
        }
};

int main()
{

    MyModel modelOne; 

    // Some internal scope
    {
        // I want modelTwo to have its own set of copied, not referenced 
        // operations, and at the same time I need RAII to for the operations, 
        // deleting them automatically as soon as it goes out of scope. 
        // This saves me from writing destructors for different concrete models.  
        MyModel modelTwo (modelOne); 
    }


    return 0;
}

最佳答案

如果您接受对您的类型的一些要求,则无需为所有类型实现虚拟克隆功能即可完成。特殊要求是类型具有可访问的复制构造函数,有些人认为这是不受欢迎的,因为可能会发生意外切片。不过,正确使用交友可能会减轻其缺点。

如果可以接受,可以通过删除提供复制功能的接口(interface)下的派生类型来解决此问题:

template <typename Base>
struct clonable {
    // virtual copy
    // this clone function will be generated via templates
    // no boilerplate is involved
    virtual std::unique_ptr<clonable<Base>> clone() const = 0;

    // expose the actual data
    virtual Base* get() = 0;
    virtual Base const* get() const = 0;

    // virtual destructor
    // note that this also obviates the need for a virtual destructor on Base
    // I would probably still make it virtual, though, just in case
    virtual ~clonable() = default;
};

此接口(interface)由在最派生类型上模板化的类实现,因此知道如何通过复制构造函数进行正常复制。

template <typename Base, typename Derived>
struct clonable_holder : clonable<Base> {
    // I suppose other constructors could be provided
    // like a forwarding one for emplacing, but I am going for minimal here
    clonable_holder(Derived value)
    : storage(std::move(value)) {}

    // here we know the most derived type, so we can use the copy constructor
    // without risk of slicing
    std::unique_ptr<clonable<Base>> clone() const override {
        return std::unique_ptr<clonable<Base>>(new clonable_holder(storage));
    }

    Base* get() override { return &storage; }
    Base const* get() const override { return &storage; }

private:
    Derived storage;
};

这将为我们生成虚拟复制函数,无需额外的样板文件。现在我们可以在此基础上构建一个类似智能指针的类(不是智能指针,因为它不提供指针语义,而是提供值语义)。

template <typename Base>
struct polymorphic_value {
    // this constructor captures the most derived type and erases it
    // this is a point where slicing may still occur
    // so making it explicit may be desirable
    // we could force constructions through a forwarding factory class for extra safety
    template <typename Derived>
    polymorphic_value(Derived value)
    : handle(new clonable_holder<Base, Derived>(std::move(value))) {
        static_assert(std::is_base_of<Base, Derived>::value,
            "value must be derived from Base");
    }

    // moving is free thanks to unique_ptr
    polymorphic_value(polymorphic_value&&) = default;
    polymorphic_value& operator=(polymorphic_value&&) = default;

    // copying uses our virtual interface
    polymorphic_value(polymorphic_value const& that)
    : handle(that.handle->clone()) {}
    polymorphic_value& operator=(polymorphic_value const& that) {
        handle = that.handle->clone();
        return *this;
    }

    // other useful constructors involve upcasting and so on

    // and then useful stuff for actually using the value
    Base* operator->() { return handle.get(); }
    Base const* operator->() const { return handle.get(); }
    // ...

private:
    std::unique_ptr<clonable<Base>> handle;
};

这只是一个最小的界面,但可以很容易地从这里充实它以涵盖更多的使用场景。

关于c++ - 如何使用智能指针作为类属性来复制对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13912310/

相关文章:

c++ - 在 MSVC 中导出模板真的有用吗?

通过 SSL 连接到 IBM MQ 的 C++ 客户端

c++ - MI 和隐式复制构造函数错误(原为 : Under what conditions can a template be the copy constructor?)

c++ - 如何用android ndk和eclipse编译c++11代码?

c++ - std::unique 指针和自定义 lambda 删除器错误

c++在复制构造函数中使用const?

c++ - 部分模板特化没有给出正确的结果

c++ - 使用 Boost shared_ptr 的嵌入式引用计数

c++ - 从派生类调用的复制构造函数

c++ - 为什么我的复制构造函数不起作用?