บุญธรรมจากที่นี่
เทมเพลตส่วนใหญ่ในไลบรารีมาตรฐาน C ++ ต้องการให้อินสแตนซ์นั้นมีประเภทสมบูรณ์ อย่างไรก็ตามshared_ptr
และunique_ptr
เป็นข้อยกเว้นบางส่วน สมาชิกบางคน แต่ไม่ใช่ทั้งหมดสามารถสร้างอินสแตนซ์ที่มีชนิดไม่สมบูรณ์ แรงจูงใจในการทำเช่นนี้คือการสนับสนุนสำนวนเช่นpimplโดยใช้พอยน์เตอร์อัจฉริยะและไม่เสี่ยงต่อพฤติกรรมที่ไม่ได้กำหนด
พฤติกรรมที่ไม่ได้กำหนดสามารถเกิดขึ้นได้เมื่อคุณมีประเภทที่ไม่สมบูรณ์และคุณโทรหาdelete
มัน:
class A;
A* a = ...;
delete a;
ข้างต้นเป็นรหัสทางกฎหมาย มันจะรวบรวม คอมไพเลอร์ของคุณอาจมีหรือไม่มีการปล่อยคำเตือนสำหรับโค้ดด้านบนเช่นด้านบน เมื่อดำเนินการสิ่งที่ไม่ดีอาจเกิดขึ้นได้ หากคุณโชคดีมากโปรแกรมของคุณจะขัดข้อง อย่างไรก็ตามผลลัพธ์ที่เป็นไปได้มากกว่าคือโปรแกรมของคุณจะปิดหน่วยความจำอย่างเงียบ ๆ โดย~A()
ไม่เรียกใช้
การใช้auto_ptr<A>
ในตัวอย่างด้านบนไม่ได้ช่วยอะไร คุณยังคงได้รับพฤติกรรมที่ไม่ได้กำหนดเหมือนกับว่าคุณใช้ตัวชี้แบบดิบ
อย่างไรก็ตามการใช้คลาสที่ไม่สมบูรณ์ในบางสถานที่นั้นมีประโยชน์มาก! นี่คือที่shared_ptr
และunique_ptr
ความช่วยเหลือ การใช้พอยน์เตอร์อัจฉริยะอย่างใดอย่างหนึ่งเหล่านี้จะช่วยให้คุณสามารถหลีกเลี่ยงประเภทที่ไม่สมบูรณ์ยกเว้นในกรณีที่จำเป็นต้องมีประเภทที่สมบูรณ์ และที่สำคัญที่สุดเมื่อจำเป็นต้องมีชนิดที่สมบูรณ์คุณจะได้รับข้อผิดพลาดในการคอมไพล์เวลาหากคุณพยายามใช้ตัวชี้สมาร์ทที่มีประเภทไม่สมบูรณ์ในจุดนั้น
ไม่มีพฤติกรรมที่ไม่ได้กำหนดเพิ่มเติม:
หากรหัสของคุณรวบรวมแสดงว่าคุณใช้ประเภทสมบูรณ์ทุกที่ที่คุณต้องการ
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptr
และunique_ptr
ต้องการประเภทที่สมบูรณ์ในสถานที่ต่างกัน เหตุผลที่คลุมเครือเกี่ยวข้องกับ deleter แบบไดนามิกและ deleter แบบคงที่ เหตุผลที่แม่นยำนั้นไม่สำคัญ ในความเป็นจริงในรหัสส่วนใหญ่มันไม่ได้เป็นสิ่งสำคัญสำหรับคุณที่จะรู้ว่าประเภทที่สมบูรณ์ต้อง เพียงแค่โค้ดและถ้าคุณเข้าใจผิดคอมไพเลอร์จะบอกคุณ
อย่างไรก็ตามในกรณีที่เป็นประโยชน์กับคุณนี่คือตารางที่จัดทำเอกสารสมาชิกหลายคนshared_ptr
และunique_ptr
เกี่ยวกับข้อกำหนดครบถ้วน หากสมาชิกต้องการชนิดที่สมบูรณ์รายการจะมี "C" มิฉะนั้นรายการในตารางจะเต็มไปด้วย "I"
Complete type requirements for unique_ptr and shared_ptr
unique_ptr shared_ptr
+------------------------+---------------+---------------+
| P() | I | I |
| default constructor | | |
+------------------------+---------------+---------------+
| P(const P&) | N/A | I |
| copy constructor | | |
+------------------------+---------------+---------------+
| P(P&&) | I | I |
| move constructor | | |
+------------------------+---------------+---------------+
| ~P() | C | I |
| destructor | | |
+------------------------+---------------+---------------+
| P(A*) | I | C |
+------------------------+---------------+---------------+
| operator=(const P&) | N/A | I |
| copy assignment | | |
+------------------------+---------------+---------------+
| operator=(P&&) | C | I |
| move assignment | | |
+------------------------+---------------+---------------+
| reset() | C | I |
+------------------------+---------------+---------------+
| reset(A*) | C | C |
+------------------------+---------------+---------------+
ดำเนินการใด ๆ ที่กำหนดให้แปลงตัวชี้ต้องใช้ชนิดที่สมบูรณ์แบบสำหรับทั้งสองและunique_ptr
shared_ptr
unique_ptr<A>{A*}
คอนสตรัคได้รับไปด้วยไม่สมบูรณ์แต่ถ้าคอมไพเลอร์ไม่จำเป็นต้องตั้งค่าการเรียกร้องให้A
~unique_ptr<A>()
ตัวอย่างเช่นถ้าคุณใส่ในกองที่คุณสามารถรับไปกับการไม่สมบูรณ์unique_ptr
A
รายละเอียดเพิ่มเติมในประเด็นนี้สามารถพบได้ในBarryTheHatchet ของคำตอบที่นี่
shared_ptr
unique_ptr
คือ Howard "ประเภทที่ไม่สมบูรณ์และ/ " ตารางของท้ายที่สุดควรตอบคำถามของคุณ