std :: shared_ptr ของสิ่งนี้


104

ขณะนี้ฉันกำลังพยายามเรียนรู้วิธีใช้ตัวชี้อัจฉริยะ อย่างไรก็ตามในขณะที่ทำการทดลองบางอย่างฉันพบสถานการณ์ต่อไปนี้ซึ่งฉันไม่สามารถหาวิธีแก้ปัญหาที่น่าพอใจได้:

สมมติว่าคุณมีวัตถุของคลาส A เป็นผู้ปกครองของวัตถุคลาส B (เด็ก) แต่ทั้งสองควรรู้จักกัน:

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

คำถามคือออบเจ็กต์ของคลาส A จะส่งผ่านstd::shared_ptrตัวมันเอง ( this) ไปยังลูกของมันได้อย่างไร?

มีโซลูชันสำหรับตัวชี้ที่ใช้ร่วมกันของ Boost ( การboost::shared_ptrหาคำตอบthis ) แต่จะจัดการสิ่งนี้โดยใช้std::ตัวชี้อัจฉริยะได้อย่างไร


2
เช่นเดียวกับเครื่องมืออื่น ๆ ที่คุณต้องใช้เมื่อมันเหมาะสม การใช้ตัวชี้อัจฉริยะสำหรับสิ่งที่คุณกำลังทำไม่ใช่
YePhIcK

ในทำนองเดียวกันเพื่อเพิ่ม ดูที่นี่ .
juanchopanza

1
นี่คือปัญหาในระดับที่เป็นนามธรรม คุณไม่รู้ด้วยซ้ำว่า "สิ่งนี้" ชี้ไปที่หน่วยความจำบนฮีป
Vaughn Cato

ภาษาไม่ได้ แต่คุณทำ ตราบใดที่คุณติดตามว่าอะไรอยู่ที่ไหนคุณก็สบายดี
Alex

คำตอบ:


173

มีไว้std::enable_shared_from_thisแค่นี้ คุณได้รับมรดกและคุณสามารถโทร.shared_from_this()จากในชั้นเรียนได้ นอกจากนี้คุณกำลังสร้างการอ้างอิงแบบวงกลมที่นี่ซึ่งอาจนำไปสู่การรั่วไหลของทรัพยากร ที่สามารถแก้ไขได้ด้วยการใช้std::weak_ptr. ดังนั้นรหัสของคุณอาจมีลักษณะเช่นนี้ (สมมติว่าเด็ก ๆ อาศัยการมีอยู่ของผู้ปกครองไม่ใช่ในทางอื่น):

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

อย่างไรก็ตามโปรดทราบว่าการโทรนั้น.shared_from_this()ต้องการให้thisเป็นของstd::shared_ptrที่จุดโทร ซึ่งหมายความว่าคุณไม่สามารถสร้างออบเจ็กต์ดังกล่าวบนสแต็กได้อีกต่อไปและโดยทั่วไปไม่สามารถเรียกใช้.shared_from_this()จากภายในตัวสร้างหรือตัวทำลาย


2
ขอบคุณสำหรับคำอธิบายของคุณและที่ชี้ให้เห็นปัญหาการพึ่งพาแบบวงกลมของฉัน
Icarus

@Deduplicator คุณหมายถึงอะไร?
yuri kilochek

1
@Deduplicator นั่นคือให้อภัย pun ของฉันตัวชี้ที่ใช้ร่วมกันค่อนข้างไม่มีจุดหมาย ตัวสร้างนั้นมีไว้เพื่อใช้กับพอยน์เตอร์ไปยังสมาชิกของอ็อบเจ็กต์ที่ถูกจัดการหรือฐานของมัน อะไรคือประเด็นของคุณ (ฉันขอโทษ)? สิ่งที่ไม่ได้เป็นเจ้าของเหล่านี้ไม่shared_ptrเกี่ยวข้องกับคำถามนี้ shared_from_thisเงื่อนไขเบื้องต้นของระบุไว้อย่างชัดเจนว่าวัตถุนั้นจะต้องเป็นเจ้าของ (ไม่ใช่แค่ชี้ไปที่) โดยบางคนshared_ptrที่จุดโทร
yuri kilochek

1
@kazarey เป็นเจ้าของโดยshared_ptrไม่จำเป็นต้องมีที่จุดของสาย แต่ในรูปแบบการใช้โดยทั่วไปคือสิ่งที่ชอบshared_ptr<Foo> p(new Foo());, shared_ptrถือว่าเป็นเจ้าของของวัตถุเท่านั้นหลังจากที่มันถูกสร้างขึ้นมาอย่างเต็มที่ เป็นไปได้ที่จะหลีกเลี่ยงสิ่งนี้โดยการสร้างshared_ptrในตัวสร้างที่เริ่มต้นthisและจัดเก็บไว้ที่อื่นที่ไม่ใช่ในพื้นที่ (เช่นในอาร์กิวเมนต์อ้างอิง) ดังนั้นจึงไม่ตายเมื่อตัวสร้างเสร็จสมบูรณ์ แต่สถานการณ์ที่ซับซ้อนนี้ไม่น่าจะจำเป็น
yuri kilochek

1
ให้ความสำคัญกับคำตอบนี้สำหรับการตอบคำถาม OPs ผู้ที่ไม่ แต่แนะนำ "ทางที่ดีกว่า" มักจะเข้าใจผิด เพิ่มจำนวนขึ้นด้วยเพราะฉันต้องการคำตอบสำหรับปัญหานี้ สำนวนที่ฉันเคยใช้หลายครั้งคือการใส่ "this" ในคอนเทนเนอร์เพื่อให้ได้รายการอินสแตนซ์คลาสทั้งหมดเช่นการเดินคอนเทนเนอร์เพื่อประมวลผลทุกอินสแตนซ์ โปรดอย่าออกแรงอธิบายว่าเหตุใดจึงเป็นปัญหา ฉันรู้ปัญหาและวิธีหลีกเลี่ยง
rm1948

8

คุณมีปัญหาหลายอย่างในการออกแบบซึ่งดูเหมือนจะมาจากความเข้าใจผิดเกี่ยวกับตัวชี้อัจฉริยะ

ตัวชี้อัจฉริยะใช้เพื่อประกาศความเป็นเจ้าของ คุณทำลายสิ่งนี้ด้วยการประกาศว่าทั้งพ่อและแม่เป็นเจ้าของลูกทุกคน แต่เด็กแต่ละคนก็เป็นพ่อแม่ ทั้งสองไม่สามารถเป็นจริงได้

getChild()นอกจากนี้คุณยังจะกลับมาเป็นตัวชี้ที่อ่อนแอใน คุณกำลังประกาศว่าผู้โทรไม่ควรสนใจเรื่องความเป็นเจ้าของ ตอนนี้สิ่งนี้สามารถ จำกัด ได้มาก แต่ด้วยการทำเช่นนั้นคุณต้องแน่ใจว่าเด็กที่มีปัญหาจะไม่ถูกทำลายในขณะที่ยังคงมีตัวชี้ที่อ่อนแออยู่หากคุณจะใช้ตัวชี้อัจฉริยะมันจะถูกแยกออกเอง .

และสิ่งสุดท้าย โดยปกติแล้วเมื่อคุณยอมรับเอนทิตีใหม่คุณควรยอมรับตัวชี้ดิบ ตัวชี้อัจฉริยะอาจมีความหมายในตัวเองสำหรับการแลกเปลี่ยนเด็กระหว่างพ่อแม่ แต่สำหรับการใช้งานทั่วไปคุณควรยอมรับคำชี้ดิบ


ดูเหมือนว่าฉันต้องชี้แจงความเข้าใจเกี่ยวกับตัวชี้อัจฉริยะจริงๆ ขอบคุณที่ชี้ให้เห็น
Icarus
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.