如何使用智能指针作为类属性来复制对象? [英] How to approach copying objects with smart pointers as class attributes?

查看:231
本文介绍了如何使用智能指针作为类属性来复制对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

提升库文档,我阅读了以下内容:

From the boost library documentation I read this:


从概念上讲,智能指针被视为拥有指向的对象
,因此负责删除对象,当它不再是
需要。

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的类的指针属性是可复制和可分配。

I have a very simple problem: I want to use RAII for pointer attributes of a class that is Copyable and Assignable.

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

The copy and assignment operations should be deep: every object should have its own copy of the actual data. Also, RTTI needs to be available for the attributes (their type may also be determined at runtime).

我应该搜索一个可复制的智能指针的实现(数据很小,所以我不需要 Copy on Write 指针),或者我将复制操作委托给我的对象的复制构造函数,如此答案

Should I be searching for an implementation of a Copyable smart pointer (the data are small, so I don't need Copy on Write pointers), or do I delegate the copy operation to the copy constructors of my objects as shown in this answer?

我可以选择哪个智能指针用于可复制和可分配类的简单RAII? (我认为unique_ptr与委托复制/赋值操作的类复制构造函数和赋值运算符将作出正确的选择,但我不知道)

Which smart pointer do I choose for simple RAII of a class that is copyable and assignable? (I'm thinking that the unique_ptr with delegated copy/assignment operations to the class copy constructor and assignment operator would make a proper choice, but I am not sure)

这里

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


推荐答案

您的类型,这可以在不需要为所有类型实现虚拟克隆功能的情况下完成。特定的要求是类型具有可访问的拷贝构造函数,由于偶然切片的可能性,一些被认为是不期望的。正确使用友好可以减轻这种缺陷。

If you accept some requirements on your types, this can be done without requiring implementing virtual clone functions for all types. The particular requirements are that the types have accessible copy constructors, which some would deem undesirable because of potential for accidental slicing. Proper use of friending may mitigate the drawbacks of that, though.

如果这样做是可以接受的,可以通过在提供复制功能的接口下擦除派生类型来实现:

If such is acceptable one can go about this by erasing the derived types under an interface that provides copy functionality:

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

这个接口是通过最类派生类型模板化的类实现的,因此知道如何使正常

This interface is implemented by a class templated on the most derived type, and thus knows how to make normal copies through the copy constructor.

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

这将为我们生成虚拟副本函数,而不需要额外的样板。现在我们可以在这之上构建一个类似智能指针的类(不是一个聪明的指针,因为它不会给出指针的语义,而是值的语义)。

This will generate virtual copy functions for us without extra boilerplate. Now we can build a smart pointer-like class on top of this (not quite a smart pointer because it does not give pointer semantics, but value semantics instead).

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

这只是一个最小的界面,但它可以轻松地从这里扩展到更多的使用场景。

This is just a minimal interface, but it can easily be fleshed out from here to cover more usage scenarios.

这篇关于如何使用智能指针作为类属性来复制对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆