คำถามนี้เหมาะกับชื่อจริงๆ: ฉันอยากรู้ว่าอะไรคือเหตุผลทางเทคนิคสำหรับความแตกต่างนี้ แต่ยังมีเหตุผลด้วย?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
คำถามนี้เหมาะกับชื่อจริงๆ: ฉันอยากรู้ว่าอะไรคือเหตุผลทางเทคนิคสำหรับความแตกต่างนี้ แต่ยังมีเหตุผลด้วย?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
คำตอบ:
เป็นเพราะstd::shared_ptr
ใช้การลบประเภทในขณะที่std::unique_ptr
ไม่
เนื่องจากstd::shared_ptr
ใช้การลบประเภทจึงสนับสนุนคุณสมบัติที่น่าสนใจอีกอย่างหนึ่งได้แก่ มันไม่ได้จำเป็นต้องมีประเภทของ Deleter ที่เป็นประเภทแม่แบบการโต้เถียงกับแม่แบบชั้นเรียน ดูคำประกาศของพวกเขา:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
ซึ่งมีDeleter
เป็นพารามิเตอร์ type ในขณะที่
template<class T>
class shared_ptr;
ไม่มีเลย
คำถามคือเหตุใดจึงshared_ptr
ใช้ type-erasure มันเป็นเช่นนั้นเพราะต้องรองรับการนับการอ้างอิงและเพื่อรองรับสิ่งนี้จึงต้องจัดสรรหน่วยความจำจากฮีปและเนื่องจากต้องจัดสรรหน่วยความจำต่อไปจึงไปอีกขั้นหนึ่งและใช้การลบประเภท - ซึ่งต้องการฮีป จัดสรรด้วย. โดยพื้นฐานแล้วมันเป็นแค่การฉวยโอกาส!
เนื่องจากการลบประเภทstd::shared_ptr
สามารถรองรับสองสิ่ง:
void*
, แต่มันก็ยังคงสามารถลบวัตถุในการทำลายอย่างถูกต้องโดยถูกต้องอัญเชิญ destructorได้เลย นั่นคือทั้งหมดที่เกี่ยวกับวิธีการstd::shared_ptr
ทำงาน
ตอนนี้คำถามคือสามารถstd::unique_ptr
จัดเก็บวัตถุเป็น void*
? คำตอบคือใช่หากคุณส่ง deleter ที่เหมาะสมเป็นอาร์กิวเมนต์ นี่คือการสาธิตดังกล่าว:
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
เอาท์พุท ( การสาธิตออนไลน์ ):
959 located at 0x18aec20 is being deleted
คุณถามคำถามที่น่าสนใจมากในความคิดเห็น:
ในกรณีของฉันฉันจะต้องใช้ตัวลบการลบประเภท แต่ดูเหมือนว่าจะเป็นไปได้เช่นกัน (โดยมีค่าใช้จ่ายในการจัดสรรฮีปบางส่วน) โดยทั่วไปหมายความว่ามีจุดเฉพาะสำหรับสมาร์ทพอยน์เตอร์ประเภทที่ 3 ซึ่งเป็นสมาร์ทพอยน์เตอร์ที่เป็นเจ้าของเฉพาะตัวพร้อมการลบประเภท
ซึ่ง@Steve Jessopแนะนำวิธีแก้ปัญหาต่อไปนี้
ฉันไม่เคยลองสิ่งนี้จริง ๆ แต่บางทีคุณอาจทำได้โดยใช้ตัวเลือกที่เหมาะสม
std::function
กับประเภท deleter ด้วยunique_ptr
? สมมติว่าใช้งานได้จริงแสดงว่าคุณทำเสร็จแล้วความเป็นเจ้าของ แต่เพียงผู้เดียวและตัวลบประเภทที่ถูกลบ
ทำตามคำแนะนำนี้ฉันใช้สิ่งนี้ (แม้ว่าจะไม่ได้ใช้ประโยชน์std::function
เนื่องจากดูเหมือนว่าไม่จำเป็น):
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
เอาท์พุท ( การสาธิตออนไลน์ ):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
หวังว่าจะช่วยได้
std::function
เป็นประเภท deleter ด้วยunique_ptr
? สมมติว่าใช้งานได้จริงแสดงว่าคุณทำเสร็จแล้วความเป็นเจ้าของ แต่เพียงผู้เดียวและตัวลบประเภทที่ถูกลบ
หนึ่งในเหตุผลคือหนึ่งในกรณีการใช้งานจำนวนมากของ a shared_ptr
- คือตัวบ่งชี้อายุการใช้งานหรือแมวมอง
สิ่งนี้ถูกกล่าวถึงในเอกสารการเพิ่มต้นฉบับ:
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
closure_target
สิ่งนี้อยู่ที่ไหน:
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
ผู้โทรจะลงทะเบียนการโทรกลับในลักษณะนี้:
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
เนื่องจากshared_ptr<X>
สามารถเปลี่ยนแปลงได้ตลอดเวลาshared_ptr<void>
event_emitter จึงสามารถไม่รู้ชนิดของวัตถุที่เรียกกลับเข้ามาได้อย่างมีความสุข
ข้อตกลงนี้จะปล่อยสมาชิกไปยังผู้ปล่อยเหตุการณ์ของภาระหน้าที่ในการจัดการข้ามกรณี (จะเกิดอะไรขึ้นหากการเรียกกลับในคิวรอให้ดำเนินการในขณะที่ active_object หายไป) และยังหมายความว่าไม่จำเป็นต้องซิงโครไนซ์การยกเลิกการสมัคร weak_ptr<void>::lock
เป็นการดำเนินการที่ซิงโครไนซ์
std::unique_ptr<void, D>
ยังคงเป็นไปได้โดยการD
ระบุ