2014-04-07 73 views
2

考慮以下父/子對象模型。父母和孩子的意圖是使用shared_ptr來管理他們的一生。父母應保留shared_ptr(保留)其子女,並且子女將保留weak_ptr給父母。我應該如何構建一個用std :: shared_ptr進行管理的實例?

鑑於這些對象的意圖總是由std::shared_ptr來管理,構建它們的最佳方式是什麼?我想到的方法(到目前爲止)感覺有點笨拙:我使用工廠(朋友)函數和一個私有構造函數來減少指向這些對象的原始指針「逃逸」的可能性。然後,孩子在ctor中創建shared_ptrthis,並將其放入父母的vector兒童中。當ctor將原始指針返回到工廠函數時,工廠函數使用shared_from_this()來獲得shared_ptr,該shared_ptr與子級的父代向量中的shared_ptr「掛鉤到」(即共享引用計數)。

下面是我想出迄今:

class Child; // Forward decl 

class Parent : std::enable_shared_from_this<Parent> { 
public:     
    int value() const { return _value; }; 
    void set_value(int newValue) { _value = newValue; }; 

    std::vector<std::shared_ptr<const Child>> children() const { 
     // propagate const-ness to the child pointers we hand back. 
     return std::vector<std::shared_ptr<const Child>>(begin(_children), end(_children)); 
    }; 

    std::vector<std::shared_ptr<Child>> children() { 
     return _children; 
    }; 

private: 

    Parent(int value) : _value(value) {}; 

    friend class Child; // So it can add itself to the _children vector 
    friend class std::shared_ptr<Parent>; // So that I don't have to inherit public from enable_shared_from_this 
    friend std::shared_ptr<Parent> CreateParent(int value); // So it can call the private ctor 

    std::vector<std::shared_ptr<Child>> _children; 
    int _value; 
}; 

class Child : std::enable_shared_from_this<Child> 
{ 
public: 
    int value() const { return _value; }; 
    void set_value(int newValue) { _value = newValue; }; 

private: 
    Child(const std::shared_ptr<Parent>& parent, int value) : _parent(parent), _value(value) { 
     std::shared_ptr<Child> sp(this); // This feels wrong, but the existence of the shared_ptr in the parent's vector of children ensures that the precondition for calling shared_from_this() is met 
     parent->_children.push_back(sp); 
    }; 

    friend std::shared_ptr<Child> CreateChild(const std::shared_ptr<Parent>& parent, int value); // So it cal call the private ctor 
    friend class std::shared_ptr<Child>; // So that I don't have to inherit public from enable_shared_from_this 

    std::weak_ptr<Parent> _parent; 
    int _value; 
}; 

std::shared_ptr<Parent> CreateParent(int value) { 
    return std::shared_ptr<Parent>(new Parent(value)); 
}; 

std::shared_ptr<Child> CreateChild(const std::shared_ptr<Parent>& parent, int value) { 
    std::shared_ptr<Child> rv = (new Child(parent, value))->shared_from_this(); 
    return rv; 
}; 

這似乎是工作,而人,是什麼感覺笨重。有沒有更好的辦法?

+1

您可以跳過您嘗試解決的原始問題的詳細信息。爲什麼生指針會「逃跑」?這是您試圖解決的「社會」合同/限制嗎? – mockinterface

+0

我試圖解決的原始問題是我對智能指針的最佳實踐缺乏透徹理解。是的,這個例子很有意思,但這是故意的。 – ipmcc

+0

哦。這只是一個我詢問時代碼的狀態。該創建者可能也是一個靜態成員函數。我只是偏愛自由職能,就這些。我想重申/縮小我的困惑:在Child的情況下,感覺就像我在Ctor和工廠方法中「分割」共享指針的創建一樣。另一方面,家長很清楚,在一個地方,因爲沒有其他共享所有權。這種分裂感覺很笨重。 – ipmcc

回答

2

我會做這種方式:

class Child; 

class Parent { 
public: 
    std::vector<const Child*> children() const { 
     // propagate const-ness to the child pointers we hand back. 
     // ... 
    } 

    const std::vector<std::unique_ptr<Child>>& children() { 
     return _children; 
    } 

    std::shared_ptr<Parent> create() { 
     // I'd rather use std::make_shared() here but it needs public ctor 
     return std::shared_ptr<Parent>(new Parent()); 
    } 

    std::unique_ptr<Child>& createChild() { 
     _children.emplace_back(new Child(this)); 
     return _children.back(); 
    } 

private:  
    Parent(); 

    std::vector<std::unique_ptr<Child>> _children; 
}; 

class Child 
{ 
private: 
    Child(Parent* parent) : _parent(parent) {} 

    friend class Parent; 

    Parent* _parent; 
}; 

我剝出爲清楚起見,「價值」的樣板。我也嘗試使用智能指針的最低級複雜程度,似乎可行的完成工作。我可能沒有把它完全用於你的具體用例,但你應該考慮更明確定義的生命週期/所有權語義。讓所有東西分享一切都是一團糟,導致缺乏清晰度......擁有清晰的所有權可以讓事情變得更簡單。而且由於Parent類仍然管理着每個Child的生命週期,所以不需要從Child返回到Parent的奇特指針 - 原始指針將起作用,因爲Child不會超過Parent。

+0

當某人想要迭代由'children()'返回的const向量引用時,會發生什麼?他們從'* iterator'得到什麼?一個'std :: unique_ptr &'?原因'unique_ptr'不能被複制,所以它不會被值完成,對吧? – ipmcc

+1

解引用non-const children()返回的迭代器確實會產生對unique_ptr的引用。它應該工作正常。 –

+1

+1因爲注意到孩子不會活過父母,因此不需要所有權。小挑剔:考慮在'Parent :: create'中使用'std :: make_shared'。這將需要友情make_shared,因爲構造函數是私人的,但[make_shared]的好處(http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/)應該彌補這一點。如果你的編譯器已經支持'Parent :: createChild'中的'std :: make_unique',那麼同樣的參數(儘管稍微弱一些,因爲你不會保存一個分配)。 – ComicSansMS

1

鑑於你的父母/子女管理的不舒服分裂的描述,可以簡化如下,

class Child; 

class Parent { 
private: 
    Parent() {}; 
    friend std::shared_ptr<Child> CreateChild(const std::shared_ptr<Parent>& parent); 
    std::vector<std::shared_ptr<Child>> _children; 
}; 

class Child { 
private: 
    Child(const std::shared_ptr<Parent>& parent) : _parent(parent) {}; 
    friend std::shared_ptr<Child> CreateChild(const std::shared_ptr<Parent>& parent); 
    std::weak_ptr<Parent> _parent; 
}; 

std::shared_ptr<Child> CreateChild(const std::shared_ptr<Parent>& parent) { 
    auto child = std::shared_ptr<Child>(new Child(parent)); 
    parent->_children.push_back(child); 
    return child; 
} 

還要注意,這裏沒有shared_from_this,也許有必要對您的其他用途,但沒有必要管理親子關係。

相關問題