如何使用智能指针作为类属性æ¥å¤åˆ¶å¯¹è±¡ï¼Ÿ

时间:2012-12-17 10:26:29

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

从boost library documentation我读到了这个:

  

从概念上讲,智能指针被视为拥有指å‘的对象,   因此在ä¸å†å­˜åœ¨æ—¶è´Ÿè´£åˆ é™¤å¯¹è±¡   需è¦çš„。

我有一个éžå¸¸ç®€å•çš„问题:我想使用RAII作为å¯å¤åˆ¶å’Œå¯åˆ†é…类的指针属性。

å¤åˆ¶å’Œèµ‹å€¼æ“作应该很深:æ¯ä¸ªå¯¹è±¡éƒ½åº”该有自己的实际数æ®å‰¯æœ¬ã€‚此外,RTTI需è¦å¯ç”¨äºŽå±žæ€§ï¼ˆå®ƒä»¬çš„类型也å¯ä»¥åœ¨è¿è¡Œæ—¶ç¡®å®šï¼‰ã€‚

我应该æœç´¢å¯å¤åˆ¶æ™ºèƒ½æŒ‡é’ˆçš„实现(数æ®å¾ˆå°ï¼Œæ‰€ä»¥æˆ‘ä¸éœ€è¦Copy on Write指针),或者我将å¤åˆ¶æ“作委托给我的对象的副本构造函数显示在this answer?

我å¯ä»¥é€‰æ‹©å“ªç§æ™ºèƒ½æŒ‡é’ˆç”¨äºŽå¯å¤åˆ¶å’Œå¯åˆ†é…的类的简å•RAII? (我认为带有委托å¤åˆ¶/赋值æ“作的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;
}

5 个答案:

答案 0 :(得分:5)

如果您接å—对类型的æŸäº›è¦æ±‚,则å¯ä»¥åœ¨ä¸éœ€è¦ä¸ºæ‰€æœ‰ç±»åž‹å®žçŽ°è™šæ‹Ÿå…‹éš†åŠŸèƒ½çš„情况下完æˆæ­¤æ“作。特殊è¦æ±‚是类型具有å¯è®¿é—®çš„æ‹·è´æž„造器,由于å¯èƒ½çš„æ„外切片,一些人认为这些构造器是ä¸åˆéœ€è¦çš„。但是,正确使用å‹æƒ…å¯ä»¥å‡è½»è¿™ç§ç¼ºç‚¹ã€‚

如果å¯ä»¥æŽ¥å—,å¯ä»¥é€šè¿‡åœ¨æä¾›å¤åˆ¶åŠŸèƒ½çš„ç•Œé¢ä¸‹åˆ é™¤æ´¾ç”Ÿç±»åž‹æ¥è§£å†³æ­¤é—®é¢˜ï¼š

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

此接å£ç”±æœ€è¡ç”Ÿç±»åž‹æ¨¡æ¿åŒ–的类实现,因此知é“如何通过å¤åˆ¶æž„造函数制作正常副本。

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

è¿™åªæ˜¯ä¸€ä¸ªæœ€å°çš„ç•Œé¢ï¼Œä½†å®ƒå¯ä»¥å¾ˆå®¹æ˜“地从这里充实,以涵盖更多的使用场景。

答案 1 :(得分:4)

现在有点晚了,但对于未æ¥çš„观众:在我的仅é™æ ‡é¢˜çš„资æºåº“AuroraåŠå…¶SmartPtr tutorial中有一个现æˆçš„实施方å¼ã€‚使用Aurora,通过智能指针实现深层拷è´æ˜¯å¾®ä¸è¶³é“的。以下代ç é€‚用于任何å¯å¤åˆ¶ç±»åž‹(car 1):

T

如果您的类具有指针æˆå‘˜ï¼Œåˆ™é€šå¸¸ä¸å¿…实现The Big Three / Five。

答案 2 :(得分:1)

我从未å¬è¯´è¿‡çŽ°æˆçš„实现,但你å¯ä»¥è‡ªå·±åŠ¨æ‰‹åšã€‚

首先你应该写一些模æ¿åŒ…装类,它有虚拟克隆方法,返回存储对象的副本。然åŽå†™ä¸€äº›å¯å¤åˆ¶çš„类的多æ€æŒæœ‰è€…

并且ä¸è¦å¿˜è®°æ£€æŸ¥åˆ é™¤ http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Checked_delete

答案 3 :(得分:1)

å¬èµ·æ¥éœ€è¦èƒ½å¤Ÿåˆ›å»ºä¸€ä¸ªæ™ºèƒ½æŒ‡é’ˆï¼Œæ¯æ¬¡åˆ›å»ºå¦ä¸€ä¸ªæ™ºèƒ½æŒ‡é’ˆå¯¹è±¡æ—¶éƒ½ä¼šåˆ›å»ºå¯¹è±¡çš„新副本。 (我认为,该副本是å¦â€œæ·±åº¦â€å–决于对象的构造函数;对于我们所知é“的所有,您存储的对象å¯èƒ½å…·æœ‰å¾ˆå¤šå±‚次的所有æƒï¼Œå› æ­¤â€œæ·±åº¦â€å–决于对象的å«ä¹‰å¯¹è±¡ã€‚我们的目的主è¦æ˜¯ä½ æƒ³è¦ä¸€ä¸ªåˆ›å»ºä¸€ä¸ªç‹¬ç‰¹å¯¹è±¡çš„东西,当智能指针使用å¦ä¸€ä¸ªå¼•ç”¨æž„建时,而ä¸æ˜¯åªå–出一个指å‘现有对象的指针。)

如果我已正确ç†è§£äº†è¿™ä¸ªé—®é¢˜ï¼Œé‚£ä¹ˆæ‚¨å°†éœ€è¦ä¸€ä¸ªè™šæ‹Ÿå…‹éš†æ–¹æ³•ã€‚没有其他方法å¯ä»¥æ­£ç¡®è°ƒç”¨æ´¾ç”Ÿç±»çš„构造函数。

struct Clonable {
  virtual ~Clonable() {}
  virtual Clonable* clone() = 0;
};
struct AutoPtrClonable {
  AutoPtrClonable(Clonable* cl=0) : obj(cl) { }
  AutoPtrClonable(const AutoPtrClonable& apc) : obj(apc.obj->clone()) { }
  ~AutoPtrClonable() { delete obj; }
  // operator->, operator*, etc
  Clonable* obj;
};

使用示例代ç ï¼Œå°†å…¶è½¬æ¢ä¸ºæ¨¡æ¿ç­‰

答案 4 :(得分:0)

你有两个解决方案(实际上你有更多,但这些对我æ¥è¯´æœ€æœ‰æ„义:)):

首先,您å¯ä»¥ä½¿ç”¨std::unique_ptr。这是一个很好的解决方案,因为它会强制æ¯ä¸ªæŒ‡é’ˆæœ‰ä¸€ä¸ªå®žä¾‹ã€‚ (使用std::shared_ptr代替也å¯ä»¥ï¼Œä½†å¦‚果你没有明确地添加代ç ï¼Œshared_ptrçš„å¤åˆ¶å’Œåˆ†é…将“共享†- 特别是你想é¿å…的。)

如果使用std::unique_ptr,则å¤åˆ¶æž„造函数和赋值è¿ç®—符应显å¼æ·±å±‚å¤åˆ¶ï¼ˆä½¿ç”¨æŒ‡é’ˆå¯¹è±¡æŽ¥å£ä¸­çš„虚拟clone方法,或者调用{{1}中的新è¿ç®—符构造函数)。

其次,你å¯ä»¥è‡ªå·±åŠ¨æ‰‹ã€‚它并没有什么å¤æ‚的,我们谈论的是一个å°åž‹ï¼ˆ10-20行左å³ï¼‰çš„实用程åºç±»ã€‚

就个人而言,如果我必须在一个地方使用这个智能指针类,我会使用std :: unique_ptr。å¦åˆ™ï¼ˆå¤šæŒ‡é’ˆï¼Œç›¸åŒçš„行为)我会自己滚动,因此我ä¸å¿…为许多实例é‡å¤æ·±å±‚å¤åˆ¶ï¼ˆä»¥ä¿æŒDRY原则)。