std :: shared_ptr ini

101

Saat ini saya mencoba mempelajari cara menggunakan petunjuk cerdas. Namun saat melakukan beberapa eksperimen, saya menemukan situasi berikut yang tidak dapat saya temukan solusi yang memuaskan:

Bayangkan Anda memiliki objek kelas A menjadi induk dari objek kelas B (anak), tetapi keduanya harus saling mengenal:

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

Pertanyaannya adalah bagaimana objek kelas A meneruskan a std::shared_ptrdari dirinya sendiri ( this) ke anaknya?

Ada solusi untuk Boost shared pointer ( Mendapatkan boost::shared_ptruntukthis ), tetapi bagaimana menangani ini menggunakan std::smart pointer?

Icarus
sumber
2
Seperti halnya alat lain, Anda harus menggunakannya pada saat yang tepat. Menggunakan petunjuk cerdas untuk apa yang Anda lakukan tidak
YePhIcK
Begitu pula untuk mendongkrak. Lihat disini .
juanchopanza
1
Ini adalah masalah pada level abstraksi itu. Anda bahkan tidak tahu bahwa "ini" menunjuk ke memori di heap.
Vaughn Cato
Yah, bahasanya tidak, tapi Anda melakukannya. Selama Anda melacak di mana, Anda akan baik-baik saja.
Alex

Jawaban:

168

Ada std::enable_shared_from_thishanya untuk tujuan ini. Anda mewarisinya dan Anda dapat menelepon .shared_from_this()dari dalam kelas. Selain itu, Anda membuat dependensi melingkar di sini yang dapat menyebabkan kebocoran resource. Itu bisa diatasi dengan penggunaan std::weak_ptr. Jadi kode Anda mungkin terlihat seperti ini (dengan asumsi anak-anak bergantung pada keberadaan induk dan bukan sebaliknya):

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

Namun perlu dicatat, bahwa panggilan .shared_from_this()membutuhkan yang thisdimiliki oleh std::shared_ptrsaat menelepon. Ini berarti bahwa Anda tidak dapat membuat objek seperti itu di tumpukan lagi, dan umumnya tidak dapat memanggil .shared_from_this()dari dalam konstruktor atau destruktor.

yuri kilochek
sumber
1
Terima kasih atas penjelasan Anda dan untuk menunjukkan masalah ketergantungan melingkar saya.
Icarus
@Deduplicator apa maksudmu?
yuri kilochek
Cobalah untuk membangun shared_ptrberdasarkan yang dibangun secara default shared_ptrdan apa pun yang Anda ingin tunjukkan ...
Deduplicator
1
@Deduplicator itu, maafkan permainan kata saya, pointer bersama yang agak tidak berguna. Konstruktor tersebut dimaksudkan untuk digunakan dengan pointer ke anggota objek yang dikelola atau basisnya. Bagaimanapun, apa maksud Anda (maaf)? Yang tidak memiliki shared_ptrini tidak relevan dengan pertanyaan ini. shared_from_thisPrasyarat dengan jelas menyatakan bahwa objek harus dimiliki (tidak hanya ditunjuk) oleh beberapa orang shared_ptrpada titik panggilan.
yuri kilochek
1
@kazarey Kepemilikan oleh a shared_ptrdiperlukan pada saat panggilan, tetapi dalam pola penggunaan tipikal, misalnya shared_ptr<Foo> p(new Foo());, shared_ptrmengambil kepemilikan objek hanya setelah itu sepenuhnya dibangun. Hal ini dimungkinkan untuk menghindari ini dengan membuat shared_ptrdalam konstruktor yang diinisialisasi dengan thisdan menyimpannya di suatu tempat non-lokal (misalnya dalam argumen referensi) sehingga tidak mati ketika konstruktor selesai. Tetapi skenario yang berbelit-belit ini sepertinya tidak diperlukan.
yuri kilochek
9

Anda memiliki beberapa masalah dalam desain Anda, yang tampaknya berasal dari kesalahpahaman Anda tentang petunjuk cerdas.

Pointer cerdas digunakan untuk menyatakan kepemilikan. Anda memecahkan ini dengan menyatakan bahwa kedua orang tua memiliki semua anak, tetapi juga bahwa setiap anak memiliki orang tuanya. Keduanya tidak mungkin benar.

Selain itu, Anda mengembalikan penunjuk lemah ke dalam getChild(). Dengan demikian, Anda menyatakan bahwa penelepon tidak boleh peduli dengan kepemilikan. Sekarang ini bisa sangat membatasi, tetapi juga dengan melakukan itu, Anda harus memastikan bahwa anak yang dimaksud tidak akan dihancurkan sementara petunjuk lemah masih dipegang, jika Anda akan menggunakan penunjuk cerdas, itu akan diselesaikan dengan sendirinya .

Dan hal terakhir. Biasanya, saat Anda menerima entitas baru, Anda biasanya harus menerima petunjuk mentah. Pointer cerdas dapat memiliki arti sendiri untuk menukar anak di antara orang tua, tetapi untuk penggunaan umum, Anda harus menerima petunjuk mentah.

Šimon Tóth
sumber
Sepertinya saya benar-benar perlu menjelaskan pemahaman saya tentang petunjuk cerdas. Terima kasih telah menunjukkannya.
Icarus