2012-08-17 31 views
5

Dưới đây là cách tiếp cận hợp lý và hiệu quả liên quan đến việc tạo Nội dung và trao quyền sở hữu Foo cho nó?Vượt qua tiêu chuẩn :: shared_ptr to Constructors

class Foo 
{ 
    explicit Foo(const std::shared_ptr<Stuff>& myStuff) 
     : m_myStuff(myStuff) 
    { 
    } 

    ... 

private: 
    const std::shared_ptr<Stuff> m_myStuff; 
} 

std::shared_ptr<Stuff> foosStuff(new Stuff()); 
Foo f(foosStuff); 
+0

@sellibitze typo – Baz

+6

Không thực sự, 'Foo' không * nhận * quyền sở hữu. Điểm của 'shared_ptr' là * chia sẻ * quyền sở hữu. – juanchopanza

+0

Hoặc thích 'std :: shared_ptr foosStuff (new Stuff());' – stefaanv

Trả lời

16

Kể từ khi bạn quan tâm đến hiệu quả Tôi muốn làm cho hai điểm:

shared_ptr <> là một trong nhiều loại tiêu chuẩn thư viện, nơi một xây dựng thái này là rẻ hơn so với một xây dựng bản sao. Việc tạo bản sao shared_ptr chậm hơn vì việc sao chép yêu cầu các bộ đếm tham chiếu được tăng lên một cách nguyên tử trong khi việc di chuyển shared_ptr không yêu cầu phải chạm vào dữ liệu hoặc bộ đếm được tham chiếu. Từ bài viết "Want Speed? Pass by value!" của Dave Abrahams người ta có thể biết rằng trong một số trường hợp, nó thực sự có lợi khi tham số chức năng theo giá trị. Đây là một trong những trường hợp này:

class Foo 
{ 
    explicit Foo(std::shared_ptr<Stuff> myStuff) 
    : m_myStuff(move(myStuff)) 
    {} 

    ... 

private: 
    std::shared_ptr<Stuff> m_myStuff; 
}; 

Bây giờ bạn có thể viết

Foo f (std::make_shared<Stuff>()); 

nơi đối số là tạm thời và không có shared_ptr được bao giờ sao chép (chỉ di chuyển một hoặc hai lần).

Việc sử dụng tiêu chuẩn :: make_shared ở đây có lợi thế là chỉ có một phân bổ được thực hiện. Trong trường hợp của bạn, bạn đã tự phân bổ đối tượng Stuff và phương thức khởi tạo shared_ptr cũng phải phân bổ các bộ tham chiếu và deleter một cách tự động. make_shared làm tất cả cho bạn chỉ với một phân bổ duy nhất.

+1

Tôi thích hàm make_shared đó – Baz

+1

btw: Nếu bạn thay đổi thành tiêu chuẩn :: unique_ptr bạn thực sự _HAVE_ truyền giá trị theo giá trị;) – sellibitze

+0

@sellibitze Lợi ích của việc truyền lớp shared_ptr trong hàm tạo của nó là gì nếu bạn không giữ nó. Chắc chắn trong trường hợp này tốt hơn là nên vượt qua trong một unique_ptr và có lớp xây dựng shared_ptr của nó từ đó? – jleahy

4

Vâng, điều đó hoàn toàn hợp lý. Bằng cách này, công việc liên quan đến việc quản lý con trỏ được chia sẻ sẽ chỉ cần được thực hiện một lần, thay vì hai lần nếu bạn truyền nó theo giá trị. Bạn cũng có thể cân nhắc sử dụng make_shared để tránh tạo bản sao.

std::shared_ptr<Stuff> foosStuff(std::make_shared<Stuff>()); 

Sự cải thiện duy nhất bạn có thể làm là nếu Foo là trở thành chủ sở hữu duy nhất (ví dụ. Bạn sẽ không giữ foosStuff xung quanh sau khi tạo Foo) sau đó bạn có thể chuyển sang sử dụng một std :: unique_ptr hoặc boost :: scoped_ptr (tương đương pre-C++ 11), sẽ có ít chi phí hơn.

+0

OK, tôi nên tìm hiểu thêm về các con trỏ thông minh khác vì tôi chỉ thực sự quen thuộc với shared_ptr. – Baz

+0

'scoped_ptr' là gì, nó không phải là một phần của C++? Có lẽ bạn có nghĩa là 'std :: unique_ptr'? Và tại sao các deleter tùy chỉnh? –

+0

@ChristianRau: Tôi cho rằng ý nghĩa của nó là 'boost :: scope_ptr'. Các bit deleter có thể là một tham chiếu lỗi thời cho một chỉnh sửa trước đó (tàng hình)? –

3

Nó có thể là hiệu quả hơn để có một helper make_foo:

Foo make_foo() { return Foo(std::make_shared<Stuff>()); } 

Bây giờ bạn có thể nói auto f = make_foo();. Hoặc ít nhất hãy tự mình sử dụng số điện thoại make_shared, vì kết quả shared_ptr có thể hiệu quả hơn so với kết quả được tạo từ biểu thức new. Và nếu Stuff thực sự mất đối số nhà xây dựng, một nhà xây dựng phụ trợ riêng có thể phù hợp:

struct Foo 
{ 
    template <typename ...Args> 
    static Foo make(Args &&... args) 
    { 
     return Foo(direct_construct(), std::forward<Args>(args)...); 
    }; 

private: 

    struct direct_construct{}; 

    template <typeaname ...Args> 
    Foo(direct_construct, Args &&... args) 
    : m_myStuff(std::make_shared<Stuff>(std::forward<Args>(args)...)) // #1 
    { } 
}; 

Bạn có thể quấn Foo::make vào trên make_foo, hoặc sử dụng nó trực tiếp:

auto f = Foo::make(true, 'x', Blue); 

Điều đó nói rằng, trừ khi bạn Thực sự là chia sẻ quyền sở hữu, một âm thanh std::unique_ptr<Stuff> giống như cách tiếp cận thích hợp hơn: Cả hai khái niệm đơn giản hơn nhiều và cũng hiệu quả hơn một chút. Trong trường hợp đó, bạn sẽ nói m_myStuff(new Stuff(std::forward<Args>(args)...)) trong dòng được đánh dấu #1.

+0

+1 để đề cập đến unique_ptr :) – sellibitze

Các vấn đề liên quan