কাঁচা, দুর্বল_পিটার, অনন্য_পিটার, শেয়ার্ড_সিপিআর ইত্যাদি… কীভাবে এগুলি বুদ্ধিমানের সাথে চয়ন করবেন?


33

সি ++ তে প্রচুর পয়েন্টার রয়েছে তবে সি 5+ প্রোগ্রামিংয়ে (বিশেষত Qt ফ্রেমওয়ার্ক সহ) আমি কেবল পুরানো কাঁচা পয়েন্টার ব্যবহার করি:

SomeKindOfObject *someKindOfObject = new SomeKindOfObject();

আমি জানি যে আরও অনেক "স্মার্ট" পয়েন্টার রয়েছে:

// shared pointer:
shared_ptr<SomeKindofObject> Object;

// unique pointer:
unique_ptr<SomeKindofObject> Object;

// weak pointer:
weak_ptr<SomeKindofObject> Object;

তবে তাদের সাথে কী করব এবং কাঁচা পয়েন্টারগুলির তুলনায় তারা আমাকে কী দিতে পারে সে সম্পর্কে আমার সামান্যতম ধারণা নেই।

উদাহরণস্বরূপ আমার কাছে এই শ্রেণীর শিরোনাম রয়েছে:

#ifndef LIBRARY
#define LIBRARY

class LIBRARY
{
public:
    // Permanent list that will be updated from time to time where
    // each items can be modified everywhere in the code:
    QList<ItemThatWillBeUsedEveryWhere*> listOfUselessThings; 
private:
    // Temporary reader that will read something to put in the list
    // and be quickly deleted:
    QSettings *_reader;
    // A dialog that will show something (just for the sake of example):
    QDialog *_dialog;
};

#endif 

এটি স্পষ্টভাবে পরিসীমা ছাড়াই নয় তবে এই 3 টি পয়েন্টারের প্রত্যেকটির জন্য তাদের "কাঁচা" রেখে দেওয়া কি ঠিক আছে বা আমার আরও কিছু উপযুক্ত ব্যবহার করা উচিত?

এবং দ্বিতীয় বার, কোনও নিয়োগকর্তা যদি কোডটি পড়েন, তবে আমি কী ধরণের পয়েন্টার ব্যবহার করি বা না ব্যবহার করে সে কি কঠোর হবে?


এই বিষয়টি এসও-র পক্ষে খুব উপযুক্ত। এটি ছিল ২০০৮ সালে । এবং এখানে আমি কখন ধরণের পয়েন্টার ব্যবহার করব? । আমি নিশ্চিত আপনি আরও ভাল মিল খুঁজে পেতে পারেন। এগুলি হলো শুধুমাত্র প্রথম দেখলাম
sehe

এই শ্রেণীর ধারণাগত অর্থ / অভিপ্রায় সম্পর্কে যতটা ধারণা রয়েছে তেমনি এটি তাদের আচরণ এবং বাস্তবায়নের প্রযুক্তিগত বিবরণ সম্পর্কে যতটা বুদ্ধিমান হয় তেমন কোনও এটির সীমান্তরেখার imo। গ্রহণযোগ্য উত্তর যেহেতু পূর্বের দিকে ঝুঁকছে, তাই এই যে প্রশ্নটির "পিএসই সংস্করণ" হতে পেরে আমি খুশি।
Ixrec

উত্তর:


70

একটি "কাঁচা" পয়েন্টারটি নিয়ন্ত্রণহীন এটি হল, নিম্নলিখিত লাইন:

SomeKindOfObject *someKindOfObject = new SomeKindOfObject();

... deleteসঠিক সময়ে যদি সাথে করা হয় না তবে স্মৃতি ফাঁস হবে ।

auto_ptr

এই মামলাগুলি হ্রাস করার জন্য, std::auto_ptr<>চালু করা হয়েছিল। ২০১১ এর মানদণ্ডের পূর্বে সি ++ এর সীমাবদ্ধতার কারণে, 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

প্রতিস্থাপন হিসাবে, সি ++ 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<>একই পয়েন্টারটিকে নির্দেশ করে এমন সমস্ত বস্তুর একটি রেফারেন্স গণনা করে । যখন একটি নতুন তৈরি করা হয়, গণনাটি উপরে যায়। যখন একটি ধ্বংস হয়, গণনা হ্রাস পায়। গণনা শূন্যে পৌঁছে গেলে পয়েন্টারটি deleted হয়।

সুতরাং এটি একটি সমস্যার পরিচয় করিয়ে দেয়: ডাবল-লিঙ্কযুক্ত কাঠামোগুলি বৃত্তাকার উল্লেখগুলির সাথে শেষ হয়। বলুন আমরা 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;
};

এখন, আমরা যদি Nodeকোনওটিকে সরিয়ে ফেলি, তবে এর সাথে একটি চক্রীয় রেফারেন্স রয়েছে। এটি কখনই deleteডি হবে না কারণ এর রেফারেন্স গণনা কখনই শূন্য হবে না।

এই সমস্যাটি সমাধান করার জন্য, আপনি একটি ব্যবহার করুন 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<>রেফারেন্স গণনাটি তার অবজেক্টের থেকে দূরে চিহ্নিত করে এবং এটি দ্বিতীয় বরাদ্দের অর্থ হতে পারে)। এটি সাধারণত কাম্য নয়।

সি ++ 11 সরবরাহ করে std::make_shared<>()এবং সি ++ 14 std::make_unique<>()এই সমস্যাটি সমাধান করার জন্য সরবরাহ করে:

foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());

এখন, উভয় ক্ষেত্রেই, thrower()ব্যতিক্রম ছুঁড়ে ফেলা হলেও , মেমরির ফাঁস হবে না। বোনাস হিসাবে, তার পরিচালিত অবজেক্টের মতো একই মেমরি স্পেসেmake_shared<>() তার রেফারেন্স গণনা তৈরি করার সুযোগ রয়েছে , যা উভয়ই দ্রুততর হতে পারে এবং মেমরির কয়েকটি বাইট সংরক্ষণ করতে পারে, যখন আপনাকে একটি ব্যতিক্রমী সুরক্ষা গ্যারান্টি দেয়!

Qt সম্পর্কে নোটস

তবে এটি লক্ষ করা উচিত যে Qt, যা প্রি-সি ++ 11 সংকলককে অবশ্যই সমর্থন করে, তার নিজস্ব আবর্জনা সংগ্রহের মডেল রয়েছে: অনেকেরই QObjectএমন একটি ব্যবস্থা আছে যেখানে ব্যবহারকারীর প্রয়োজন ছাড়াই সেগুলি সঠিকভাবে ধ্বংস হয়ে deleteযায়।

আমি জানি না যে QObjectসি ++ 11 পরিচালিত পয়েন্টারগুলি পরিচালনা করে কীভাবে আচরণ করবে, তাই আমি বলতে পারি না shared_ptr<QDialog>এটি একটি ভাল ধারণা। আমার কাছে নিশ্চিতভাবে বলতে কিউটিটির সাথে পর্যাপ্ত অভিজ্ঞতা নেই তবে আমি বিশ্বাস করি যে এই ব্যবহারের ক্ষেত্রে কিউটি 5 সামঞ্জস্য করা হয়েছে।


1
@ জিলিটর: দয়া করে কিউটি সম্পর্কে আমার যুক্ত মন্তব্যটি নোট করুন। তিনটি পয়েন্টারই পরিচালনা করা উচিত কিনা সে সম্পর্কে আপনার প্রশ্নের উত্তর Qt অবজেক্টগুলি ভাল আচরণ করবে কিনা তার উপর নির্ভর করে।
গ্রেফ্যাড

2
"উভয়ই পয়েন্টার ধরে রাখতে আলাদা বরাদ্দ দেয়"? না, অনন্য_পিটার কখনও অতিরিক্ত কিছু বরাদ্দ করে না, কেবল ভাগ করা_পিটার অবশ্যই একটি রেফারেন্স-কাউন্ট + বরাদ্দকারী-অবজেক্ট বরাদ্দ করতে পারে। "দুটি লাইনেই কি স্মৃতি ফুটো হবে"? না, কেবলমাত্র, খারাপ আচরণের গ্যারান্টিও নয়।
উত্সাহক

1
@ ডিডুকিপেটর: আমার শব্দটি অবশ্যই অস্পষ্ট ছিল: এড বস্তু shared_ptrথেকে এটি একটি পৃথক অবজেক্ট - একটি পৃথক বরাদ্দ new। তারা বিভিন্ন জায়গায় বিদ্যমান। make_sharedএগুলিকে একই স্থানে একত্রে রাখার দক্ষতা রয়েছে যা অন্যান্য জিনিসের সাথে ক্যাশে লোকেশনে উন্নতি করে।
গ্রেফ্যাড

2
@ গ্রেফ্যাড: নোননোনো। shared_ptrএকটি বস্তু। এবং কোনও অবজেক্ট পরিচালনা করার জন্য এটি অবশ্যই একটি (রেফারেন্স-গণনা (দুর্বল + শক্তিশালী) + ধ্বংসকারী) -অবজেক্ট বরাদ্দ করতে হবে। make_sharedএটি এবং পরিচালিত অবজেক্টটিকে এক টুকরো হিসাবে বরাদ্দ দেয়। unique_ptrসেগুলি ব্যবহার করে না, সুতরাং কোনও উপযুক্ত সুবিধা নেই, বিষয়টি সর্বদা স্মার্ট পয়েন্টারের মালিকানাধীন কিনা তা নিশ্চিত করে। একটি সরাইয়া হিসাবে, এক একটি থাকতে পারে shared_ptrযা মালিক একটি অন্তর্নিহিত বস্তু এবং একটি প্রতিনিধিত্ব করে nullptr, অথবা যার নিজের নেই এবং এটা একটা অ nullpointer প্রতিনিধিত্ব করে।
উত্সাহক

1
আমি এটি দেখেছি এবং এটি কী shared_ptrকরে তা নিয়ে একটি সাধারণ বিভ্রান্তি দেখা দেয়: ১. এটি কিছু বস্তুর মালিকানা ভাগ করে নেয় (একটি অভ্যন্তরীণ গতিবদ্ধভাবে বরাদ্দকৃত বস্তু যা দুর্বল এবং শক্তিশালী রেফারেন্স গণনা, পাশাপাশি একটি মুছক দ্বারা প্রতিনিধিত্ব করে) । 2. এটিতে একটি পয়েন্টার রয়েছে। সেই দুটি অংশই স্বাধীন। make_uniqueএবং make_sharedউভয়ই নিশ্চিত করে যে বরাদ্দ করা বস্তুটি নিরাপদে স্মার্ট পয়েন্টারে রেখে দেওয়া হয়েছে। এছাড়াও, make_sharedমালিকানা-অবজেক্ট এবং পরিচালিত পয়েন্টার একসাথে বরাদ্দ করতে দেয়।
উত্সাহক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.