ตัวชี้ "raw" ไม่มีการจัดการ นั่นคือบรรทัดต่อไปนี้:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
... จะทำให้หน่วยความจำรั่วไหลหากdelete
ไม่สามารถดำเนินการตามเวลาที่เหมาะสมได้
auto_ptr
เพื่อลดกรณีเหล่านี้std::auto_ptr<>
ถูกนำมาใช้ เนื่องจากข้อ จำกัด ของ C ++ ก่อนมาตรฐานปี 2011 แต่ก็ยังง่ายสำหรับauto_ptr
การรั่วไหลของหน่วยความจำ มันเพียงพอสำหรับกรณี จำกัด เช่นนี้อย่างไรก็ตาม:
void func() {
std::auto_ptr<SomeKindOfObject> sKOO_ptr(new SomeKindOfObject());
// do some work
// will not leak if you do not copy sKOO_ptr.
}
กรณีการใช้งานที่อ่อนแอที่สุดอย่างหนึ่งอยู่ในคอนเทนเนอร์ นี่เป็นเพราะหากมีการทำสำเนาของauto_ptr<>
และสำเนาเก่าไม่ได้ถูกตั้งค่าใหม่อย่างรอบคอบคอนเทนเนอร์อาจลบตัวชี้และข้อมูลสูญหาย
unique_ptr
เพื่อทดแทน C ++ 11 แนะนำ std::unique_ptr<>
:
void func2() {
std::unique_ptr<SomeKindofObject> sKOO_unique(new SomeKindOfObject());
func3(sKOO_unique); // now func3() owns the pointer and sKOO_unique is no longer valid
}
ดังกล่าวunique_ptr<>
จะได้รับการทำความสะอาดอย่างถูกต้องแม้เมื่อมันผ่านไปมาระหว่างฟังก์ชั่น มันทำได้โดยการแสดงความหมายของ "เจ้าของ" ของตัวชี้ - ความหมาย "เจ้าของ" ล้างมัน เหมาะสำหรับใช้ในภาชนะบรรจุ:
std::vector<std::unique_ptr<SomeKindofObject>> sKOO_vector();
ซึ่งแตกต่างจากauto_ptr<>
, unique_ptr<>
เป็นที่มีความประพฤติดีที่นี่และเมื่อvector
ปรับขนาดไม่มีวัตถุที่จะถูกลบโดยไม่ตั้งใจในขณะที่vector
สำเนาเก็บสนับสนุนของมัน
shared_ptr
และ weak_ptr
unique_ptr<>
มีประโยชน์เพื่อให้แน่ใจ แต่มีบางกรณีที่คุณต้องการให้ส่วนฐานสองส่วนของคุณสามารถอ้างถึงวัตถุเดียวกันและคัดลอกตัวชี้ไปรอบ ๆ ในขณะที่ยังรับประกันการล้างข้อมูลที่เหมาะสม ตัวอย่างเช่นต้นไม้อาจมีลักษณะเช่นนี้เมื่อใช้std::shared_ptr<>
:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
ในกรณีนี้เรายังสามารถเก็บสำเนาของโหนดรูทหลายชุดและต้นไม้จะถูกล้างอย่างเหมาะสมเมื่อสำเนาทั้งหมดของโหนดรูทถูกทำลาย
วิธีนี้ใช้งานได้เนื่องจากแต่ละตัวshared_ptr<>
ยึดไม่เพียง แต่ตัวชี้ไปยังวัตถุเท่านั้น แต่ยังนับการอ้างอิงของshared_ptr<>
วัตถุทั้งหมดที่อ้างถึงตัวชี้เดียวกัน เมื่อสร้างขึ้นใหม่การนับจะเพิ่มขึ้น เมื่อมีใครถูกทำลายการนับจะลดลง เมื่อการนับถึงศูนย์ตัวชี้คือdelete
d
ดังนั้นสิ่งนี้จึงแนะนำปัญหา: โครงสร้างที่มีการเชื่อมโยงสองด้านจบลงด้วยการอ้างอิงแบบวงกลม สมมติว่าเราต้องการเพิ่มparent
ตัวชี้ไปยังต้นไม้ของเราNode
:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
ทีนี้ถ้าเราลบ a ออกNode
มันจะมีการอ้างอิงแบบวนรอบ มันจะไม่เป็นdelete
d เพราะจำนวนการอ้างอิงของมันจะไม่เป็นศูนย์
ในการแก้ปัญหานี้คุณใช้std::weak_ptr<>
:
template<class T>
struct Node {
T value;
std::weak_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
ตอนนี้สิ่งต่าง ๆ จะทำงานได้อย่างถูกต้องและการลบโหนดจะไม่ทิ้งการอ้างอิงที่ติดอยู่กับโหนดหลักไว้ มันทำให้การเดินต้นไม้มีความซับซ้อนเพิ่มขึ้นเล็กน้อย:
std::shared_ptr<Node<T>> parent_of_this = node->parent.lock();
ด้วยวิธีนี้คุณสามารถล็อคการอ้างอิงไปยังโหนดและคุณมีการรับประกันที่สมเหตุสมผลว่ามันจะไม่หายไปในขณะที่คุณกำลังทำงานอยู่ shared_ptr<>
ของมัน
make_shared
และ make_unique
ขณะนี้มีปัญหาเล็กน้อยกับshared_ptr<>
และunique_ptr<>
ที่ควรได้รับการแก้ไข สองบรรทัดต่อไปนี้มีปัญหา:
foo_unique(std::unique_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
foo_shared(std::shared_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
หากthrower()
มีข้อผิดพลาดทั้งสองบรรทัดจะทำให้หน่วยความจำรั่ว และยิ่งกว่านั้นshared_ptr<>
ให้นับการอ้างอิงอยู่ไกลจากวัตถุที่ชี้ไปและสิ่งนี้ทำได้หมายถึงการจัดสรรครั้งที่สอง) ไม่เป็นที่พึงปรารถนา
C ++ 11 มีstd::make_shared<>()
และ C ++ 14 มีให้std::make_unique<>()
เพื่อแก้ไขปัญหานี้:
foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());
ในทั้งสองกรณีแม้ว่าจะthrower()
มีข้อผิดพลาดเกิดขึ้นจะไม่มีการรั่วไหลของหน่วยความจำ โบนัสmake_shared<>()
มีโอกาสสร้างจำนวนการอ้างอิงในพื้นที่หน่วยความจำเดียวกันเดียวกับวัตถุที่มีการจัดการซึ่งสามารถทำได้เร็วกว่าและสามารถบันทึกหน่วยความจำได้สองสามไบต์ในขณะที่ให้การรับประกันความปลอดภัยยกเว้น!
หมายเหตุเกี่ยวกับ Qt
อย่างไรก็ตามควรสังเกตว่า Qt ซึ่งต้องรองรับคอมไพเลอร์ pre-C ++ 11 มีรูปแบบการเก็บขยะของตัวเอง: หลายคนQObject
มีกลไกที่จะถูกทำลายอย่างถูกต้องโดยผู้ใช้ไม่จำเป็นต้องdelete
พวกเขา
ฉันไม่ทราบว่าQObject
จะทำงานอย่างไรเมื่อจัดการโดยตัวชี้ที่มีการจัดการ C ++ 11 ดังนั้นฉันจึงไม่สามารถพูดได้ว่าshared_ptr<QDialog>
เป็นความคิดที่ดี ฉันไม่มีประสบการณ์เพียงพอกับ Qt ที่จะพูดอย่างแน่นอน แต่ฉันเชื่อว่า Qt5 ได้รับการปรับสำหรับกรณีการใช้งานนี้