ความแตกต่างระหว่างพอยน์เตอร์ชุดต่อไปนี้คืออะไร? คุณใช้ตัวชี้แต่ละตัวในรหัสการผลิตเมื่อใด?
ตัวอย่างจะได้รับการชื่นชม!
scoped_ptr
shared_ptr
weak_ptr
intrusive_ptr
คุณใช้บูสต์ในรหัสการผลิตหรือไม่?
ความแตกต่างระหว่างพอยน์เตอร์ชุดต่อไปนี้คืออะไร? คุณใช้ตัวชี้แต่ละตัวในรหัสการผลิตเมื่อใด?
ตัวอย่างจะได้รับการชื่นชม!
scoped_ptr
shared_ptr
weak_ptr
intrusive_ptr
คุณใช้บูสต์ในรหัสการผลิตหรือไม่?
คำตอบ:
ง่ายเมื่อคุณมีคุณสมบัติที่คุณสามารถกำหนดตัวชี้สมาร์ทแต่ละตัวได้ มีคุณสมบัติที่สำคัญสามประการ
ครั้งแรกหมายความว่าตัวชี้สมาร์ทไม่สามารถลบวัตถุได้เนื่องจากมันไม่ได้เป็นเจ้าของ ที่สองหมายถึงสมาร์ทพอยน์เตอร์เดียวเท่านั้นที่สามารถชี้ไปที่วัตถุเดียวกันในเวลาเดียวกันได้ หากตัวชี้สมาร์ทจะถูกส่งกลับจากฟังก์ชั่นความเป็นเจ้าของจะถูกโอนไปยังตัวชี้สมาร์ทกลับมาเช่น
ที่สามหมายความว่าพอยน์เตอร์อัจฉริยะหลายตัวสามารถชี้ไปที่วัตถุเดียวกันในเวลาเดียวกัน สิ่งนี้นำไปใช้กับตัวชี้แบบ rawเช่นกันอย่างไรก็ตามตัวชี้แบบ raw ขาดคุณสมบัติที่สำคัญ: พวกเขาไม่ได้กำหนดว่าพวกเขาเป็นเจ้าของหรือไม่ ส่วนแบ่งของความเป็นเจ้าของตัวชี้สมาร์ทจะลบวัตถุหากเจ้าของทุกคนยอมแพ้วัตถุ พฤติกรรมนี้เกิดขึ้นบ่อยครั้งดังนั้นการเป็นเจ้าของพอยน์เตอร์อัจฉริยะจึงมีการแพร่กระจายอย่างกว้างขวาง
ตัวชี้สมาร์ทที่เป็นเจ้าของบางคนไม่สนับสนุนตัวที่สองหรือที่สาม พวกเขาจึงไม่สามารถส่งคืนจากฟังก์ชั่นหรือผ่านที่อื่น ซึ่งเหมาะที่สุดสำหรับRAII
จุดประสงค์ที่ตัวชี้อัจฉริยะเก็บไว้ในเครื่องและเพิ่งสร้างขึ้นเพื่อให้วัตถุนั้นหลุดออกจากขอบเขต
ส่วนแบ่งของความเป็นเจ้าของสามารถดำเนินการได้โดยมีตัวสร้างสำเนา สิ่งนี้จะคัดลอกตัวชี้อัจฉริยะและทั้งสำเนาและต้นฉบับจะอ้างอิงวัตถุเดียวกัน การถ่ายโอนความเป็นเจ้าของไม่สามารถดำเนินการได้จริงใน C ++ ในขณะนี้เนื่องจากไม่มีวิธีการถ่ายโอนบางสิ่งจากวัตถุหนึ่งไปยังอีกวัตถุที่ได้รับการสนับสนุนโดยภาษา: หากคุณพยายามส่งคืนวัตถุจากฟังก์ชันสิ่งที่เกิดขึ้นคือวัตถุนั้นคัดลอก ตัวชี้สมาร์ทที่ดำเนินการถ่ายโอนความเป็นเจ้าของจึงต้องใช้ตัวสร้างการคัดลอกเพื่อใช้การถ่ายโอนความเป็นเจ้าของนั้น อย่างไรก็ตามในทางกลับกันการแบ่งการใช้งานในภาชนะบรรจุเนื่องจากข้อกำหนดระบุพฤติกรรมบางอย่างของตัวสร้างสำเนาขององค์ประกอบของภาชนะบรรจุซึ่งเข้ากันไม่ได้กับพฤติกรรมที่เรียกว่า "ตัวสร้างการเคลื่อนย้าย" ของตัวชี้สมาร์ทเหล่านี้
C ++ 1x ให้การสนับสนุนพื้นเมืองสำหรับการถ่ายโอนความเป็นเจ้าของโดยการแนะนำสิ่งที่เรียกว่า "ตัวสร้างการย้าย" และ "ตัวดำเนินการกำหนดค่าการย้าย" unique_ptr
นอกจากนี้ยังมาพร้อมกับการดังกล่าวเป็นตัวชี้สมาร์ทการถ่ายโอนการเป็นเจ้าของที่เรียกว่า
scoped_ptr
เป็นตัวชี้อัจฉริยะที่ไม่สามารถถ่ายโอนหรือแบ่งปันได้ มันสามารถใช้งานได้ถ้าคุณต้องการจัดสรรหน่วยความจำภายใน แต่ต้องแน่ใจว่ามันว่างอีกครั้งเมื่อขอบเขตไม่เพียงพอ แต่มันยังสามารถสลับกับ scoped_ptr อื่นได้หากคุณต้องการ
shared_ptr
เป็นตัวชี้อัจฉริยะที่แชร์ความเป็นเจ้าของ (ประเภทที่สามด้านบน) มันคือการอ้างอิงที่ถูกนับเพื่อที่ว่ามันจะได้เห็นว่าเมื่อไหร่สำเนาล่าสุดของมันจะออกนอกขอบเขตและจากนั้นมันก็ปล่อยให้วัตถุที่ถูกจัดการ
weak_ptr
เป็นตัวชี้สมาร์ทที่ไม่ได้เป็นเจ้าของ มันถูกใช้เพื่ออ้างอิงวัตถุที่มีการจัดการ (จัดการโดย shared_ptr) โดยไม่ต้องเพิ่มจำนวนการอ้างอิง โดยปกติคุณจะต้องดึงตัวชี้ raw ออกจาก shared_ptr และคัดลอกที่อยู่รอบ ๆ แต่นั่นจะไม่ปลอดภัยเนื่องจากคุณจะไม่มีวิธีตรวจสอบเมื่อวัตถุถูกลบจริง ๆ ดังนั้น weak_ptr จัดให้มีวิธีการโดยอ้างอิงวัตถุที่จัดการโดย shared_ptr หากคุณต้องการเข้าถึงวัตถุคุณสามารถล็อคการจัดการของมัน (เพื่อหลีกเลี่ยงสิ่งนั้นในเธรดอื่นที่ shared_ptr ปล่อยให้มันเป็นอิสระในขณะที่คุณใช้วัตถุ) จากนั้นใช้มัน หาก weak_ptr ชี้ไปที่วัตถุที่ถูกลบไปแล้วมันจะสังเกตเห็นคุณโดยการโยนข้อยกเว้น การใช้ weak_ptr นั้นมีประโยชน์มากที่สุดเมื่อคุณมีการอ้างอิงแบบวนรอบ: การนับการอ้างอิงไม่สามารถรับมือกับสถานการณ์เช่นนี้ได้อย่างง่ายดาย
intrusive_ptr
เหมือนกับ shared_ptr แต่ไม่เก็บจำนวนการอ้างอิงใน shared_ptr แต่ปล่อยให้การเพิ่ม / ลดจำนวนการนับไปยังฟังก์ชันตัวช่วยบางอย่างที่จำเป็นต้องกำหนดโดยวัตถุที่ได้รับการจัดการ สิ่งนี้มีความได้เปรียบที่วัตถุที่ถูกอ้างอิงแล้ว (ซึ่งมีจำนวนการอ้างอิงเพิ่มขึ้นโดยกลไกการนับการอ้างอิงภายนอก) สามารถถูกยัดเข้าไปใน intrusive_ptr - เนื่องจากการนับการอ้างอิงไม่ได้อยู่ภายในตัวชี้สมาร์ทอีกต่อไป กลไกการนับอ้างอิง
unique_ptr
เป็นการถ่ายโอนของตัวชี้ความเป็นเจ้าของ คุณไม่สามารถคัดลอกได้ แต่คุณสามารถย้ายได้โดยใช้ตัวสร้างการย้ายของ C ++ 1x:
unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!
นี่คือความหมายที่ std :: auto_ptr เชื่อฟัง แต่เนื่องจากขาดการสนับสนุนดั้งเดิมสำหรับการย้ายจึงไม่สามารถให้บริการได้โดยไม่ผิดพลาด unique_ptr จะขโมยทรัพยากรโดยอัตโนมัติจาก unique_ptr อื่นชั่วคราวซึ่งเป็นหนึ่งในคุณสมบัติหลักของซีแมนทิกส์การย้าย auto_ptr จะถูกคัดค้านในการเปิดตัว C ++ Standard ครั้งถัดไปซึ่งเป็นการสนับสนุนของ unique_ptr C ++ 1x จะอนุญาตให้บรรจุวัตถุที่สามารถเคลื่อนย้ายได้ แต่ไม่สามารถคัดลอกลงในคอนเทนเนอร์ได้ ดังนั้นคุณสามารถใส่อะไรที่ไม่เหมือนใครเข้าไปในเวกเตอร์ได้ ฉันจะหยุดที่นี่และอ้างอิงถึงคุณในบทความที่ดีเกี่ยวกับเรื่องนี้ถ้าคุณต้องการอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้
auto_ptr
เลิกใช้แล้ว (C ++ 11)
intrusive_ptr
จะดีกว่าshared_ptr
สำหรับการเชื่อมโยงแคชที่ดีกว่า เห็นได้ชัดว่าแคชทำงานได้ดีขึ้นถ้าคุณเก็บจำนวนการอ้างอิงเป็นส่วนหนึ่งของหน่วยความจำของวัตถุที่มีการจัดการแทนที่จะเป็นวัตถุแยกต่างหาก สิ่งนี้สามารถนำไปใช้ในแม่แบบหรือ superclass ของวัตถุที่มีการจัดการ
scoped_ptrเป็นวิธีที่ง่ายที่สุด เมื่อมันออกนอกขอบเขตมันจะถูกทำลาย รหัสต่อไปนี้ผิดกฎหมาย (scoped_ptrs ไม่สามารถคัดลอกได้) แต่จะแสดงให้เห็นถึงจุดหนึ่ง:
std::vector< scoped_ptr<T> > tPtrVec;
{
scoped_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory
shared_ptrถูกนับการอ้างอิง ทุกครั้งที่มีการคัดลอกหรือการมอบหมายเกิดขึ้นจำนวนการอ้างอิงจะเพิ่มขึ้น ทุกครั้งที่มีการยิง destructor ของอินสแตนซ์จำนวนการอ้างอิงสำหรับ raw T * จะลดลง เมื่อเป็น 0 ตัวชี้จะถูกปล่อยให้เป็นอิสระ
std::vector< shared_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
// This copy to tPtrVec.push_back and ultimately to the vector storage
// causes the reference count to go from 1->2
tPtrVec.push_back(tPtr);
// num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe
weak_ptrเป็นการอ้างอิงแบบอ่อนไปยังตัวชี้ที่ใช้ร่วมกันซึ่งคุณต้องตรวจสอบเพื่อดูว่าการชี้ไปยัง shared_ptr ยังคงอยู่หรือไม่
std::vector< weak_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed = tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
cout << "Raw T* was freed, can't access it"
}
else
{
tPtrVec[0]->DoSomething(); // raw
}
โดยทั่วไปจะใช้intrusive_ptrเมื่อมีสมาร์ทโฟน ptr ของบุคคลที่สามที่คุณต้องใช้ มันจะเรียกฟังก์ชั่นฟรีเพื่อเพิ่มและลดจำนวนการอ้างอิงดูลิงค์เพื่อเพิ่มเอกสารสำหรับข้อมูลเพิ่มเติม
if (tPtrAccessed[0].get() == 0)
ควรที่จะเป็นif (tPtrAccessed.get() == 0)
อย่างไร
อย่ามองข้ามboost::ptr_container
การสำรวจเพื่อเพิ่มพอยน์เตอร์อัจฉริยะ พวกเขาสามารถประเมินค่าได้ในสถานการณ์ที่เช่นstd::vector<boost::shared_ptr<T> >
จะช้าเกินไป
ฉันสองคำแนะนำเกี่ยวกับการดูเอกสาร มันไม่น่ากลัวอย่างที่คิด และคำใบ้สั้น ๆ เล็กน้อย:
scoped_ptr
- ตัวชี้ถูกลบโดยอัตโนมัติเมื่อไม่อยู่ในขอบเขต หมายเหตุ - ไม่สามารถกำหนดได้ แต่ไม่แนะนำค่าใช้จ่ายintrusive_ptr
- ตัวชี้นับการอ้างอิงโดยไม่มีค่าใช้จ่าย smart_ptr
ตัวชี้อ้างอิงนับด้วยค่าใช้จ่ายไม่ อย่างไรก็ตามวัตถุเองเก็บนับการอ้างอิงweak_ptr
- ทำงานร่วมกับ shared_ptr
เพื่อจัดการกับสถานการณ์ที่ทำให้เกิดการพึ่งพาแบบวงกลม (อ่านเอกสารประกอบและค้นหาภาพที่ดีใน google)shared_ptr
- ตัวชี้สมาร์ทพอยน์เตอร์ทั่วไปและทรงพลังที่สุด (จากรุ่นที่เสนอโดยการเพิ่ม)auto_ptr
ที่ช่วยให้มั่นใจว่าวัตถุที่มันจะถูกทำลายโดยอัตโนมัติเมื่อการควบคุมออกจากขอบเขต อย่างไรก็ตามมันมีความหมายที่แตกต่างจากคนอื่น ๆunique_ptr
- จะมาพร้อมกับ C ++ 0xการตอบสนองต่อการแก้ไข: ใช่