std :: shared_ptr trong số này


101

Tôi hiện đang cố gắng học cách sử dụng con trỏ thông minh. Tuy nhiên, trong khi thực hiện một số thử nghiệm, tôi đã phát hiện ra tình huống sau mà tôi không thể tìm ra giải pháp thỏa mãn:

Hãy tưởng tượng bạn có một đối tượng của lớp A là cha của một đối tượng thuộc lớp B (con), nhưng cả hai nên biết nhau:

class A;
class B;

class A
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children->push_back(child);

        // How to do pass the pointer correctly?
        // child->setParent(this);  // wrong
        //                  ^^^^
    }

private:        
    std::list<std::shared_ptr<B>> children;
};

class B
{
public:
    setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    };

private:
    std::shared_ptr<A> parent;
};

Câu hỏi đặt ra là làm thế nào một đối tượng của lớp A có thể truyền một std::shared_ptrcủa chính nó ( this) cho con của nó?

Có các giải pháp cho con trỏ chia sẻ Boost ( Bắt boost::shared_ptrchothis ), nhưng làm thế nào để xử lý điều này bằng cách sử dụng std::con trỏ thông minh?


2
Đối với bất kỳ công cụ nào khác, bạn phải sử dụng nó khi thích hợp. Sử dụng con trỏ thông minh cho những gì bạn đang làm là không
YePhIcK

Tương tự để thúc đẩy. Xem tại đây .
juanchopanza

1
Đây là một vấn đề ở mức độ trừu tượng đó. Bạn thậm chí không biết rằng "this" trỏ đến bộ nhớ trên heap.
Vaughn Cato,

Ngôn ngữ thì không, nhưng bạn thì có. Miễn là bạn theo dõi những gì ở đâu, bạn sẽ ổn.
Alex

Câu trả lời:


168

std::enable_shared_from_thischỉ cho mục đích này. Bạn kế thừa từ nó và bạn có thể gọi .shared_from_this()từ bên trong lớp. Ngoài ra, bạn đang tạo ra các phụ thuộc vòng tròn ở đây có thể dẫn đến rò rỉ tài nguyên. Điều đó có thể được giải quyết với việc sử dụng std::weak_ptr. Vì vậy, mã của bạn có thể trông như thế này (giả sử trẻ em dựa vào sự tồn tại của cha mẹ chứ không phải ngược lại):

class A;
class B;

class A
    : public std::enable_shared_from_this<A>
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children.push_back(child);

        // like this
        child->setParent(shared_from_this());  // ok
        //               ^^^^^^^^^^^^^^^^^^
    }

private:     
    // note weak_ptr   
    std::list<std::weak_ptr<B>> children;
    //             ^^^^^^^^
};

class B
{
public:
    void setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    }

private:
    std::shared_ptr<A> parent;
};

Tuy nhiên, lưu ý rằng cuộc gọi .shared_from_this()yêu cầu phải thisđược sở hữu bởi std::shared_ptrtại điểm gọi. Điều này có nghĩa là bạn không thể tạo đối tượng như vậy trên ngăn xếp nữa và nói chung không thể gọi .shared_from_this()từ bên trong một hàm tạo hoặc hàm hủy.


1
Cảm ơn bạn đã giải thích và chỉ ra vấn đề phụ thuộc vòng tròn của tôi.
Icarus

@Deduplicator ý bạn là gì?
yuri kilochek

Cố gắng xây dựng một shared_ptrcăn cứ trên một mặc định-xây dựng shared_ptrvà bất cứ điều gì bạn muốn chỉ nó ở ...
Deduplicator

1
@Deduplicator đó là một, thứ lỗi cho con trỏ được chia sẻ khá vô nghĩa của tôi. Hàm tạo đó được thiết kế để sử dụng với các con trỏ tới các thành viên của đối tượng được quản lý hoặc các cơ sở của nó. Trong mọi trường hợp, điểm của bạn là gì (tôi xin lỗi)? Những không sở hữu shared_ptrkhông liên quan đến câu hỏi này. shared_from_thisCác điều kiện tiên quyết nói rõ rằng đối tượng phải được sở hữu (không chỉ đến) bởi một số người shared_ptrtại điểm gọi.
yuri kilochek

1
@kazarey Quyền sở hữu bởi a shared_ptrlà bắt buộc tại điểm gọi, nhưng trong một kiểu sử dụng điển hình, tức là một cái gì đó giống như shared_ptr<Foo> p(new Foo());, chỉ shared_ptrthừa nhận quyền sở hữu đối tượng sau khi nó được xây dựng hoàn chỉnh. Có thể phá vỡ điều này bằng cách tạo shared_ptrtrong phương thức khởi tạo được khởi tạo thisvà lưu trữ nó ở một nơi nào đó không cục bộ (ví dụ trong một đối số tham chiếu) để nó không chết khi phương thức khởi tạo hoàn thành. Nhưng kịch bản phức tạp này không chắc là cần thiết.
yuri kilochek

9

Bạn có một số vấn đề trong thiết kế của mình, dường như xuất phát từ việc bạn hiểu sai về con trỏ thông minh.

Con trỏ thông minh được sử dụng để khai báo quyền sở hữu. Bạn đang phá vỡ điều này bằng cách tuyên bố rằng cả cha và mẹ đều sở hữu tất cả con cái, nhưng mỗi đứa trẻ đều sở hữu nó là cha mẹ. Cả hai đều không thể đúng.

Ngoài ra, bạn đang trả về một con trỏ yếu trong getChild(). Làm như vậy, bạn đang tuyên bố rằng người gọi không nên quan tâm đến quyền sở hữu. Bây giờ điều này có thể rất hạn chế, nhưng cũng như làm như vậy, bạn phải đảm bảo rằng đứa trẻ được đề cập sẽ không bị phá hủy trong khi bất kỳ con trỏ yếu nào vẫn được giữ, nếu bạn sử dụng một con trỏ thông minh, nó sẽ tự được sắp xếp .

Và điều cuối cùng. Thông thường, khi bạn chấp nhận các thực thể mới, bạn thường nên chấp nhận các con trỏ thô. Con trỏ thông minh có thể có ý nghĩa riêng trong việc hoán đổi con cái giữa cha mẹ, nhưng đối với cách sử dụng chung, bạn nên chấp nhận con trỏ thô.


Có vẻ như tôi thực sự cần làm rõ hiểu biết của mình về con trỏ thông minh. Cảm ơn bạn đã chỉ ra rằng.
Icarus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.