প্রশ্নটি সত্যই শিরোনামে ফিট করে: আমি জানতে আগ্রহী যে এই পার্থক্যের প্রযুক্তিগত কারণটি কী, তবে যুক্তিও কি?
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
টাইপ প্যারামিটার হিসাবে আছে , যখন
template<class T>
class shared_ptr;
এটা আছে না।
এখন প্রশ্ন হচ্ছে, shared_ptr
টাইপ-ইরেজোর প্রয়োগ কেন ? ভাল, এটি এটি করে, কারণ এটি রেফারেন্স-গণনা সমর্থন করতে হবে, এবং এটি সমর্থন করার জন্য, এটি গাদা থেকে মেমরি বরাদ্দ করতে হয় এবং যেহেতু এটি যেভাবেই মেমরি বরাদ্দ করতে হয়, এটি আরও এক ধাপ এগিয়ে যায় এবং টাইপ-ইরেজরের প্রয়োগ করে - যার জন্য গাদা প্রয়োজন which বরাদ্দও। সুতরাং মূলত এটি কেবল সুবিধাবাদী হচ্ছে!
টাইপ-ইরেজরের কারণে, std::shared_ptr
দুটি জিনিস সমর্থন করতে সক্ষম:
void*
, কিন্তু এখনও এটি সঠিকভাবে ধ্বংস বস্তু মুছে ফেলতে সক্ষম হয় সঠিকভাবে দ্বারা তাদের বিনাশকারী invoking ।ঠিক আছে. এটি কীভাবে std::shared_ptr
কাজ করে সে সম্পর্কে ।
এখন প্রশ্ন, std::unique_ptr
জিনিস হিসাবে সংরক্ষণ করতে পারেন void*
? ঠিক আছে, উত্তরটি হ্যাঁ - আপনি যদি যুক্তি হিসাবে একটি উপযুক্ত মুছকটি পাস করেন। এখানে এইরকম একটি বিক্ষোভ রয়েছে:
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
আপনি মন্তব্যে একটি খুব আকর্ষণীয় প্রশ্ন জিজ্ঞাসা করেছেন:
আমার ক্ষেত্রে আমার একটি টাইপ মোছার মুছে ফেলার দরকার হবে তবে এটি সম্ভবত সম্ভব (কিছু গাদা বরাদ্দের ব্যয়ে)। মূলত, এর অর্থ কি এখানে আসলে তৃতীয় ধরণের স্মার্ট পয়েন্টারটির জন্য একটি বিশেষ স্পট রয়েছে: ধরণের ক্ষয়ের সাথে একচেটিয়া মালিকানা স্মার্ট পয়েন্টার।
যার প্রতি @ স্টিভ জেসোপ নিম্নলিখিত সমাধানের পরামর্শ দিয়েছেন
আমি আসলে এটি কখনও চেষ্টা করে দেখিনি, তবে সম্ভবত আপনি
std::function
মুছে ফেলার মতো উপযুক্ত হিসাবে ব্যবহার করে তা অর্জন করতে পারেন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
মুছে যাওয়ার ধরণ হিসাবে উপযুক্ত হিসাবে ব্যবহার করে এটি অর্জন করতে পারেন unique_ptr
? মনে করুন যে এটি আসলে আপনার কাজ হয়ে গেলে কাজ করে, একচেটিয়া মালিকানা এবং একটি টাইপ-মুছানো মুছক।
যুক্তিগুলির মধ্যে একটির অনেকগুলি ব্যবহারের ক্ষেত্রে একটি হয় 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>
তাই ইভেন্ট_মিটার এখন যে ধরণের বস্তুতে আবার কল করছে তা আনন্দের সাথে অজানা থাকতে পারে।
এই ব্যবস্থাটি ক্রসিং কেসগুলি পরিচালনা করার বাধ্যবাধকতার ইভেন্ট ইমেটারের গ্রাহকদের মুক্তি দেয় (যদি সারি-এ কলব্যাক সক্রিয় থাকে, সক্রিয়_বজেক্ট চলে যাওয়ার সময় ব্যবস্থা নেওয়া হবে কি?) এবং এর অর্থ হ'ল আনসাবস্ক্রিপশন সিঙ্ক্রোনাইজ করার দরকার নেই। weak_ptr<void>::lock
একটি সিঙ্ক্রোনাইজড অপারেশন।
std::unique_ptr<void, D>
এখনও একটি উপযুক্ত প্রদানের মাধ্যমে সম্ভবD
।