ความแตกต่างคือstd::make_shared
ทำการจัดสรรฮีปหนึ่งครั้งขณะที่การเรียกคอนstd::shared_ptr
สตรัคเตอร์ทำสองค่า
การจัดสรรฮีปเกิดขึ้นที่ไหน?
std::shared_ptr
จัดการสองหน่วยงาน:
- บล็อกควบคุม (เก็บข้อมูลเมตาเช่นการนับการอ้างอิงการลบประเภทการลบเป็นต้น)
- วัตถุที่กำลังจัดการ
std::make_shared
ดำเนินการบัญชีการจัดสรรฮีปเดียวสำหรับพื้นที่ที่จำเป็นสำหรับทั้งบล็อกควบคุมและข้อมูล ในกรณีอื่นnew Obj("foo")
เรียกใช้การจัดสรรฮีปสำหรับข้อมูลที่มีการจัดการและตัวstd::shared_ptr
สร้างจะดำเนินการอีกอันสำหรับบล็อกควบคุม
สำหรับข้อมูลเพิ่มเติมโปรดดูที่บันทึกการดำเนินงานที่cppreference
Update I: Exception-Safety
หมายเหตุ (2019/08/30) : นี่ไม่ใช่ปัญหาตั้งแต่ C ++ 17 เนื่องจากการเปลี่ยนแปลงในลำดับการประเมินผลของฟังก์ชันอาร์กิวเมนต์ โดยเฉพาะอย่างยิ่งอาร์กิวเมนต์แต่ละตัวในฟังก์ชั่นจะต้องดำเนินการอย่างเต็มที่ก่อนการประเมินผลของข้อโต้แย้งอื่น ๆ
เนื่องจาก OP ดูเหมือนจะสงสัยเกี่ยวกับข้อยกเว้นด้านความปลอดภัยของสิ่งต่าง ๆ ฉันจึงอัปเดตคำตอบของฉัน
ลองพิจารณาตัวอย่างนี้
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
เนื่องจาก C ++ อนุญาตให้มีคำสั่งย่อยของการประเมินผลของคำสั่งย่อยโดยพลคำสั่งหนึ่งที่เป็นไปได้คือ:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
ทีนี้สมมติว่าเราได้รับข้อผิดพลาดเกิดขึ้นในขั้นตอนที่ 2 (เช่นออกจากข้อยกเว้นหน่วยความจำตัวRhs
สร้างโยนข้อยกเว้นบางอย่าง) เราสูญเสียหน่วยความจำที่จัดสรรในขั้นตอนที่ 1 เนื่องจากไม่มีสิ่งใดจะมีโอกาสล้างมัน แก่นของปัญหาตรงนี้คือตัวชี้แบบดิบไม่ได้ถูกส่งผ่านไปยังตัวstd::shared_ptr
สร้างทันที
วิธีหนึ่งในการแก้ไขปัญหานี้คือการทำในบรรทัดที่แยกต่างหาก
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
วิธีที่ดีที่สุดในการแก้ไขปัญหานี้คือการใช้std::make_shared
แทน
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
อัปเดต II: ข้อเสียของ std::make_shared
เธซเธฑเคซี่ย์เมนต์ 's:
เนื่องจากมีการจัดสรรเพียงครั้งเดียวหน่วยความจำของปวงจึงไม่สามารถจัดสรรคืนได้จนกว่าบล็อกควบคุมจะไม่ใช้งานอีกต่อไป A weak_ptr
สามารถทำให้บล็อกควบคุมยังมีชีวิตอยู่ได้ตลอดไป
เหตุใดอินสแตนซ์ของweak_ptr
ทำให้บล็อกควบคุมยังมีชีวิตอยู่
จะต้องมีวิธีการในweak_ptr
การกำหนดว่าวัตถุที่ได้รับการจัดการยังคงใช้ได้หรือไม่ (เช่นสำหรับlock
) พวกเขาทำสิ่งนี้โดยการตรวจสอบจำนวนของshared_ptr
วัตถุที่มีการจัดการซึ่งถูกเก็บไว้ในบล็อกควบคุม ผลลัพธ์คือบล็อกควบคุมยังมีชีวิตอยู่จนกระทั่งshared_ptr
จำนวนและweak_ptr
จำนวนนับทั้งสองเข้าชม 0
กลับไปยัง std::make_shared
เนื่องจากstd::make_shared
ทำการจัดสรรฮีปเดียวสำหรับทั้งบล็อกควบคุมและวัตถุที่ได้รับการจัดการจึงไม่มีวิธีใดที่จะเพิ่มหน่วยความจำสำหรับบล็อกควบคุมและวัตถุที่ได้รับการจัดการอย่างอิสระ เราจะต้องรอจนกว่าเราสามารถฟรีทั้งบล็อกควบคุมและการจัดการวัตถุซึ่งจะเกิดขึ้นจนกว่าจะไม่มีshared_ptr
หรือweak_ptr
s ยังมีชีวิตอยู่
สมมติว่าเราทำการจัดสรรฮีปสองชุดสำหรับบล็อกควบคุมและอ็อบเจ็กต์ที่ถูกจัดการผ่านnew
และตัวshared_ptr
สร้าง จากนั้นเราเพิ่มหน่วยความจำสำหรับวัตถุที่มีการจัดการ (อาจก่อนหน้านี้) เมื่อไม่มีshared_ptr
ชีวิตและเพิ่มหน่วยความจำสำหรับบล็อกควบคุม (อาจจะภายหลัง) เมื่อไม่มีweak_ptr
ชีวิต