Downcasting shared_ptr <Base> เป็น shared_ptr <Derived>?


103

อัปเดต: shared_ptr ในตัวอย่างนี้เหมือนใน Boost แต่ไม่รองรับ shared_polymorphic_downcast (หรือ dynamic_pointer_cast หรือ static_pointer_cast สำหรับเรื่องนั้น)!

ฉันพยายามเริ่มต้นตัวชี้ที่ใช้ร่วมกันไปยังคลาสที่ได้รับโดยไม่สูญเสียจำนวนอ้างอิง

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

จนถึงตอนนี้ดีมาก ฉันไม่ได้คาดหวังว่า C ++ จะแปลง Base * เป็น Derived * โดยปริยาย อย่างไรก็ตามฉันต้องการฟังก์ชันที่แสดงโดยรหัส (นั่นคือรักษาการนับการอ้างอิงในขณะที่ดาวน์แคสต์ตัวชี้ฐาน) ความคิดแรกของฉันคือการจัดหาผู้ดำเนินการแคสต์ใน Base เพื่อให้การแปลงโดยปริยายเป็น Derived สามารถเกิดขึ้นได้ (สำหรับ pedants: ฉันจะตรวจสอบว่าดาวน์คาสต์ถูกต้องไม่ต้องกังวล):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

มันไม่ได้ช่วยอะไร ดูเหมือนว่าคอมไพเลอร์ไม่สนใจตัวดำเนินการพิมพ์คาสต์ของฉันโดยสิ้นเชิง มีความคิดอย่างไรที่ฉันจะทำให้งาน shared_ptr ทำงานได้ สำหรับคะแนนพิเศษ: Base* constเป็นประเภทใด? const Base*ฉันเข้าใจ แต่Base* const? อะไรconstหมายถึงในกรณีนี้หรือไม่?


ทำไมคุณต้องใช้ shared_ptr <Derived> แทน shared_ptr <Base>
บิล

3
เนื่องจากฉันต้องการเข้าถึงฟังก์ชันใน Derived ที่ไม่ได้อยู่ใน Base โดยไม่ต้องโคลนอ็อบเจ็กต์ (ฉันต้องการอ็อบเจ็กต์เดียวอ้างอิงโดยพอยน์เตอร์ที่ใช้ร่วมกันสองตัว) ยังไงซะทำไมผู้ดำเนินรายการไม่ทำงาน
Lajos Nagy

คำตอบ:


112

คุณสามารถใช้dynamic_pointer_cast. std::shared_ptrได้รับการสนับสนุนโดย

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

เอกสารประกอบ: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

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

- อัปเดต:หากเป็นประเภทที่ไม่มีความหลากหลายstd::static_pointer_castอาจใช้


5
std::shared_ptrผมไม่เข้าใจจากบรรทัดแรกว่าเขาไม่ได้ใช้ std::shared_ptrแต่จากความคิดเห็นของคำตอบแรกที่ผมสรุปว่าเขาไม่ได้ใช้เพิ่มเพื่อให้เขาอาจจะใช้
Massood Khaari

ตกลง. ขออภัย. เขาควรจะชี้แจงได้ดีขึ้นว่าเขาใช้การปรับใช้แบบกำหนดเอง
Massood Khaari

47

ฉันคิดว่าคุณกำลังใช้boost::shared_ptr... ฉันคิดว่าคุณต้องการdynamic_pointer_castหรือshared_polymorphic_downcastหรือ

อย่างไรก็ตามสิ่งเหล่านี้จำเป็นต้องมีประเภทหลายรูปแบบ

สิ่งที่ชนิดของประเภทBase* constคืออะไร? const Base*ฉันเข้าใจ แต่Base* const? อะไรconstหมายถึงในกรณีนี้หรือไม่?

  • const Base * เป็นตัวชี้ที่ไม่แน่นอนของค่าคงที่ Baseเป็นตัวชี้ผันแปรไปอย่างต่อเนื่อง
  • Base const * เป็นตัวชี้ที่ไม่แน่นอนของค่าคงที่ Baseเป็นตัวชี้ผันแปรไปอย่างต่อเนื่อง
  • Base * const เป็นตัวชี้ค่าคงที่ไปสู่การผันแปร Baseเป็นตัวชี้ไปยังคงไม่แน่นอน
  • Base const * constBaseเป็นตัวชี้คงที่คงที่

นี่คือตัวอย่างเล็กน้อย:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

ฉันไม่แน่ใจว่าเป็นความตั้งใจที่ตัวอย่างของคุณสร้างอินสแตนซ์ของประเภทพื้นฐานและโยนมันออกมา แต่มันแสดงให้เห็นถึงความแตกต่างได้อย่างดี

static_pointer_cast"จะทำมัน" สิ่งนี้จะส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด ( Derived*ชี้ไปที่หน่วยความจำที่จัดสรรและเริ่มต้นโดยBase) และอาจทำให้เกิดปัญหาหรือแย่ลง การอ้างอิงนับบนbaseจะเพิ่มขึ้น

dynamic_pointer_castจะส่งผลให้ตัวชี้โมฆะ การอ้างอิงนับบนbaseจะไม่เปลี่ยนแปลง

shared_polymorphic_downcastจะมีผลเช่นเดียวกับนักแสดงที่คงที่ แต่จะเรียกการยืนยันมากกว่าดูเหมือนจะประสบความสำเร็จและนำไปสู่พฤติกรรมที่ไม่ได้กำหนด จำนวนอ้างอิงbaseจะเพิ่มขึ้น

ดู(ลิงก์ตาย) :

บางครั้งมันก็ยากเล็กน้อยที่จะตัดสินใจว่าจะใช้static_castหรือdynamic_castและคุณหวังว่าคุณจะมีโลกทั้งสองใบเล็กน้อย เป็นที่ทราบกันดีอยู่แล้วว่า dynamic_cast มีค่าใช้จ่ายในการรันไทม์ แต่จะปลอดภัยกว่าในขณะที่ static_cast ไม่มีค่าใช้จ่ายใด ๆ เลย แต่อาจล้มเหลวโดยไม่โต้ตอบ จะดีแค่ไหนถ้าคุณสามารถใช้shared_dynamic_castในการดีบักบิวด์และshared_static_castในบิลด์รุ่น shared_polymorphic_downcastดีเช่นสิ่งที่มีอยู่แล้วและถูกเรียกว่า


น่าเสียดายที่โซลูชันของคุณขึ้นอยู่กับฟังก์ชัน Boost ที่ถูกแยกออกโดยเจตนาจากการใช้งาน shared_ptr ที่เราใช้อยู่ (อย่าถามว่าทำไม) สำหรับคำอธิบาย const ตอนนี้มันสมเหตุสมผลกว่ามาก
Lajos Nagy

3
ขาดการใช้ตัวshared_ptrสร้างอื่น ๆ(การใช้static_cast_tagและdynamic_cast_tag) คุณสามารถทำได้ไม่มากนัก สิ่งที่คุณทำภายนอกshared_ptrจะไม่สามารถจัดการการอ้างอิงได้ - ในการออกแบบ OO ที่ "สมบูรณ์แบบ" คุณสามารถใช้ประเภทพื้นฐานได้ตลอดเวลาและไม่จำเป็นต้องรู้หรือสนใจว่าประเภทที่ได้รับมาคืออะไรเนื่องจากฟังก์ชันทั้งหมดของมันถูกเปิดเผยผ่านอินเทอร์เฟซระดับพื้นฐาน บางทีคุณอาจจะต้องคิดใหม่ว่าทำไมคุณถึงต้องแคสต์ตั้งแต่แรก
Tim Sylvester

1
@ Tim Sylvester: แต่ C ++ ไม่ใช่ภาษา OO ที่ "สมบูรณ์แบบ"! :-) down-casts มีที่มาในภาษา OO ที่ไม่สมบูรณ์แบบ
Steve Folly

5

ถ้ามีคนมาที่นี่ด้วย boost :: shared_ptr ...

นี่คือวิธีที่คุณสามารถดาวน์คาสต์ไปยัง Boost shared_ptr ที่ได้รับมา สมมติว่าได้รับมาจากฐาน

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

ตรวจสอบให้แน่ใจว่าคลาส / โครงสร้าง 'ฐาน' มีฟังก์ชันเสมือนอย่างน้อยหนึ่งฟังก์ชัน ตัวทำลายเสมือนยังใช้งานได้

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