স্মার্ট পয়েন্টার (Smart Pointers)
যে ক্লিনার (Janitor) কখনোই কিছু ভোলে না (Never Forgets)
কোনো একটি কনফারেন্স রুমের (conference room) কথা কল্পনা করুন। মানুষজন সেখানে হেঁটে বেড়াচ্ছে (walk in), হোয়াইটবোর্ড (whiteboard) ব্যবহার করছে, আর কফি পান করছে (drink coffee)। আর সেখানকার শেষ মানুষটিও (last person) যখন ওই রুম থেকে বেরিয়ে যায় (leaves), তখন সেখানকার ক্লিনার (janitor) নিজে থেকেই এর দরজাটি বন্ধ করে দেয় (locks the door) এবং রুমটিকে পরিষ্কার করে ফেলে (cleans up)। এক্ষেত্রে অন্য কাউকে এটি মনে রাখতে হয় না — এটি শুধু নিজে থেকেই ঘটে যায় (just happens)।
এটিকে মূলত RAII (Resource Acquisition Is Initialization) বলা হয়। আর সি++ (C++)-এ এই স্মার্ট পয়েন্টারগুলোই (smart pointers) হলো সেই ক্লিনার (janitor)। এদের মূলত হিপের (heap) ওপর বিভিন্ন অবজেক্টকে অ্যালোকেট (allocate) বা বরাদ্দ করার ক্ষমতা বা ওন (own) করার অধিকার দেওয়া থাকে, আর এই স্মার্ট পয়েন্টারগুলো (smart pointer) যখনই তাদের নিজস্ব জগৎ বা স্কোপ (scope) থেকে বেরিয়ে যায়, তখন তারা মূলত তাদের অধীনে থাকা যেকোনো অবজেক্টকেই সাথে সাথে বা বলা যায় অটোমেটিক্যালি ডিলিট (automatically deletes) করে দেয়। যার ফলে এখানে কোনো কিছুকে ম্যানুয়ালি ডিলিটের (manual delete) প্রয়োজন পড়ে না। কোনো মেমোরি লিকের (memory leaks) চিন্তা থাকে না। কোনো ডাবল ফ্রি-এরও (double-frees) ভয় থাকে না。
কেন এই র পয়েন্টার (raw new/delete) এত বিপদজনক (Dangerous)?
ক্লিনারের (janitor) এই উদাহরণে র পয়েন্টার (raw pointers) দিয়ে মূলত আপনাকেই (you) বোঝানো হয়। আর মানুষ মাত্রই ভুল বা ভুলে যাওয়ার (humans forget) সম্ভাবনা থাকে।
- কোনো কিছুকে
deleteকরতে ভুলে গেছেন (Forget todelete)? তার মানে মেমোরি লিক (Memory leak)। - একই জিনিসকে দুইবার মুছে ফেললে (Delete twice)? তার মানে এটি আনডিফাইন্ড বিহেভিয়ার (Undefined behavior)।
newএবংdelete-এর মাঝে কোনো এক্সেপশন থ্রো (Exception thrown) হয়েছে? এর মানে লিক (Leak)।- কোনো ফাংশন থেকে খুব শুরুতেই ফিরে এলে (Return early)? তার মানে লিক (Leak)।
এই স্মার্ট পয়েন্টারগুলো (Smart pointers) মূলত আপনার এই সমস্ত সমস্যারই সমাধান নিয়ে আসে। চলুন এদের তিনটি ধরন (three kinds) সম্পর্কে বিস্তারিত জেনে নেওয়া যাক。
ইউনিক পয়েন্টার (unique_ptr) — এর একচ্ছত্র মালিকানা (Exclusive Ownership)
এই std::unique_ptr হলো অনেকটা কোনো প্রাইভেট লকারের (private locker) চাবির (key) মতো। একটি নির্দিষ্ট সময়ে শুধুমাত্র একটি (Only one) ইউনিক পয়েন্টারই (unique_ptr) কোনো একটি নির্দিষ্ট অবজেক্টের মালিক (own) হতে পারে। এটি যখন কোনো কারণে মুছে যায় বা ধ্বংস (destroyed) হয়ে যায়, তখন মূলত এর ওই মূল অবজেক্টটিও সম্পূর্ণভাবে মুছে (deleted) যায়। আপনি চাইলেই একে কপি (copy) করতে পারবেন না — তবে আপনি চাইলে এটিকে নিজের সুবিধামতো মুভ (move) বা স্থানান্তর করতে পারবেন, যা মূলত ওই চাবিটি অন্য কাউকে দিয়ে (handing the key) তার মালিকানা পরিবর্তন (transferring ownership) করে দেওয়ার মতোই কাজ করে。
ইউনিক পয়েন্টার (unique_ptr) — এদের তৈরি (Creation) বা মুভ করা (Move)
শেয়ার্ড পয়েন্টার (shared_ptr) — এর যৌথ মালিকানা (Shared Ownership)
এই std::shared_ptr-কে মূলত একটি শেয়ার করা বা ব্যবহৃত নেটফ্লিক্স (Netflix) অ্যাকাউন্টের সাথে তুলনা করা যেতে পারে। এক্ষেত্রে একাধিক শেয়ার্ড পয়েন্টারগুলো (shared_ptrs) একই অবজেক্টের (same object) দিকে পয়েন্ট (point) করে থাকতে পারে। এটি নিজস্ব কাজের জন্য মূলত একটি রেফারেন্স কাউন্ট (reference count) মেন্টেইন বা ধরে রাখে। যখন এর একেবারে শেষের (last) শেয়ার্ড পয়েন্টারটি ধ্বংস (destroyed) হয়ে যায়, তখন মূলত ঠিক ওই ক্লিনারের (janitor) মতোই এটি ওই অবজেক্টটিকে মুছে (deleted) ফেলে — যখন এর সবশেষ মানুষটিও (last person) ঘর থেকে বেরিয়ে যায়。
রেফারেন্স কাউন্টিংয়ের (Reference Counting) সাথে শেয়ার্ড পয়েন্টার (shared_ptr)
উইক পয়েন্টার (weak_ptr) — এক ধরনের অজার্ভার বা দর্শক (The Observer)
এই std::weak_ptr-কে মূলত দূরবীন (binoculars) হাতে দাঁড়িয়ে থাকা কোনো দর্শকের (spectator) সাথে তুলনা করা যেতে পারে — এটি মূলত ওই শেয়ার্ড পয়েন্টারের (shared_ptr) মালিকানাধীন অবজেক্টটিকে (object) দেখতে (see) পারে, কিন্তু এটি কখনোই একে জীবিত (keep alive) রাখতে পারে না। এটি মূলত এর এই সার্কুলার রেফারেন্সিংগুলোকে (circular references) ভেঙে দেয় বা ব্রেক করে (breaks) (যেখানে দুটি শেয়ার্ড পয়েন্টার বা shared_ptrs একে অপরের দিকে পয়েন্ট করে থাকে, এবং এরা কেউই কখনো এর কাউন্টে (count) গিয়ে জিরো বা शून्य-তে পৌঁছায় না)।
এখানকার এই অবজেক্টটিকে ব্যবহার (use) করতে চাইলে, আপনাকে অবশ্যই .lock()-টি কল (call) করতে হবে, যা মূলত আপনাকে একটি অস্থায়ী শেয়ার্ড পয়েন্টারগামী (temporary shared_ptr) পথ প্রদান করবে — তবে তা শুধুমাত্র তখন, যদি সেখানকার ওই অবজেক্টটি তখনও বিদ্যমান (still exists) বা বেঁচে থাকে。
ইউনিক পয়েন্টার (unique_ptr) থেকে শেয়ার্ড পয়েন্টারে (shared_ptr) কনভার্ট বা রূপান্তর (Converting) করা
কাস্টম ডিলিটার (Custom Deleter)
একটি ইউনিক পয়েন্টার বা unique_ptr-কে রিটার্ন (Returning) করে আসা ফ্যাক্টরি ফাংশন (Factory Function)
কুইক রেফারেন্স (Quick Reference)
| স্মার্ট পয়েন্টার (Smart Pointer) | মালিকানা বা ওনারশিপ (Ownership) | কপি করা যায় কি না? (Copyable?) | কখন ব্যবহার করবেন (Use When) |
|---|---|---|---|
unique_ptr | একচ্ছত্র বা Exclusive (1 মালিক বা owner) | না (শুধু মুভ করা যায় বা move only) | যেকোনো কিছু নির্বাচনের সময় এটিই ডিফল্ট পছন্দ (Default choice)। একক মালিকানা বা Single owner। |
shared_ptr | শেয়ার্ড বা Shared (একাধিক বা N মালিক বা owners) | হ্যাঁ (Yes) | বিভিন্ন ধরনের কোড যখন তাদের কোনো একটি নির্দিষ্ট রিসোর্সকে শেয়ার (share) করে |
weak_ptr | অজার্ভার বা দর্শক (Observer) (কোনো মালিক নেই বা 0 owners) | হ্যাঁ (Yes) | যেকোনো সার্কেলকে ব্রেক (Breaking cycles) করার সময়, ক্যাশ (caches), এবং বিভিন্ন অপশনাল অ্যাক্সেসগুলোর (optional access) সময় |
make_unique এবং make_shared ব্যবহার করার চেষ্টা করুন — এগুলো মূলত যেকোনো ধরনের এক্সেপশনের জন্য সম্পূর্ণ নিরাপদ (exception-safe) এবং এগুলো তুলনামূলক অনেকটাই পরিষ্কার বা ক্লিনার (cleaner)। যদি আপনি কখনো নিজের কোডে 'new' লিখতে যান, তবে তার মানে হলো আপনি জিনিসটিকে ভুল (wrong) উপায়ে করছেন। এর একমাত্র ব্যতিক্রম (exception) হলো যখন unique_ptr-এর সাহায্যে আপনার কোনো কাস্টম ডিলিটারের (custom deleter) প্রয়োজন পড়ে, ঠিক তখনই কেবল আপনাকে সরাসরি এই 'new'-টি ব্যবহার (use) করতে হয়।