`সক্ষম_শ্রেড_ফর্ম_তাদের কী উপযোগিতা?


349

enable_shared_from_thisবুস্ট.অ্যাসিওর উদাহরণগুলি পড়ার সময় আমি দৌড়ে গিয়েছিলাম এবং ডকুমেন্টেশন পড়ার পরেও কীভাবে এটি সঠিকভাবে ব্যবহার করা উচিত তার জন্য আমি এখনও হারিয়ে গিয়েছি। এই ক্লাসটি ব্যবহার করার সময় কী বোঝায় সে সম্পর্কে কেউ দয়া করে আমাকে একটি উদাহরণ এবং ব্যাখ্যা দিতে পারেন।

উত্তর:


362

এটি আপনাকে একটি বৈধ shared_ptrউদাহরণ পেতে সক্ষম করে this, যখন আপনার সমস্ত কিছু থাকে this। তা ছাড়া, আপনি যদি একটি পাবার কোন উপায় থাকবে shared_ptrকরার this, যদি না আপনি ইতিমধ্যে একজন সদস্য হিসেবে এক ছিল। সক্ষম_শ্রেড_ফর্ম_এর জন্য বুস্ট ডকুমেন্টেশন থেকে এই উদাহরণ :

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

পদ্ধতিটি f()কোনও বৈধ ফেরত দেয় shared_ptr, যদিও এর কোনও সদস্যের উদাহরণ নেই instance মনে রাখবেন আপনি কেবল এটি করতে পারবেন না:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

এই ভাগ করা পয়েন্টার যে এই ফিরে এসেছে তার "যথাযথ" একটির থেকে আলাদা রেফারেন্স গণনা থাকবে এবং অবজেক্টটি মোছার সাথে সাথে তার মধ্যে একটি হারাতে হবে এবং ঝুঁকতে থাকা রেফারেন্সটি ধারণ করবে।

enable_shared_from_thisসি ++ 11 মানের অংশে পরিণত হয়েছে। আপনি এটি সেখান থেকে পাশাপাশি বুস্ট থেকেও পেতে পারেন।


202
+1 টি। মূল বক্তব্যটি হ'ল কেবল শেয়ারড_পিটার <ওয়াইস (এটি) ফিরিয়ে দেওয়ার "সুস্পষ্ট" কৌশলটি ভেঙে গেছে, কারণ এই বাতাসটি পৃথক রেফারেন্স গণনা সহ একাধিক স্বতন্ত্র শেয়ার্ড_প্টর অবজেক্ট তৈরি করে। এই কারণে আপনাকে কখনই একই কাঁচা পয়েন্টার থেকে একাধিক শেয়ারড_পিটার তৈরি করতে হবে না ।
j_random_hacker

3
এটি লক্ষ করা উচিত যে সি ++ 11 এবং তারপরে , এটি কোনও উত্তরাধিকার সূত্রে প্রাপ্ত হলে কাঁচা পয়েন্টারে কোনও কনস্ট্রাক্টর ব্যবহার করা পুরোপুরি বৈধ । বুস্টের শব্দার্থকগুলি এটি সমর্থন করার জন্য আপডেট হয়েছিল কিনা তা আমি জানি নাstd::shared_ptr std::enable_shared_from_this
ম্যাথু

6
@ ম্যাথহেহোল্ডার আপনার কাছে এর জন্য কোন উদ্ধৃতি রয়েছে? Cppreferences.com এ আমি পড়েছি " std::shared_ptrঅন্যের দ্বারা ইতিমধ্যে পরিচালিত কোনও অবজেক্টের জন্য একটি নির্মাণ করা std::shared_ptrঅভ্যন্তরীণভাবে সঞ্চিত দুর্বল রেফারেন্সের সাথে পরামর্শ করবে না এবং এভাবে অপরিবর্তিত আচরণের দিকে পরিচালিত করবে।" ( en.cppreferences.com/w/cpp/memory/enable_shared_from_this )
থরবজর্ন লিন্ডাইজার

5
আপনি শুধু করতে পারবেন না কেন shared_ptr<Y> q = p?
ড্যান এম।

2
@ থরবজর্নলিন্ডিজার, আপনি ঠিক বলেছেন, এটি সি ++ 17 এবং তারপরে। কিছু বাস্তবায়ন প্রকাশের আগে সি ++ 16 শব্দার্থবিজ্ঞান অনুসরণ করেছিল। সি ++ 11 থেকে সি ++ 14 এর জন্য সঠিক হ্যান্ডলিংটি ব্যবহার করা উচিত std::make_shared<T>
ম্যাথু

198

দুর্বল পয়েন্টারগুলির বিষয়ে ডাঃ ডবস নিবন্ধ থেকে, আমি মনে করি এই উদাহরণটি বোঝা সহজ (উত্স: http://drdobbs.com/cpp/184402026 ):

... এর মতো কোড সঠিকভাবে কাজ করবে না:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

দুজনের কেউই নেই shared_ptr বস্তুর কোনওটিই অন্যটির সম্পর্কে জানে না, তাই উভয়ই যখন তারা ধ্বংস হয়ে যায় তখন সংস্থানটি ছেড়ে দেওয়ার চেষ্টা করবে। এটি সাধারণত সমস্যার দিকে পরিচালিত করে।

একইভাবে, যদি কোনও সদস্য ফাংশনটির জন্য এমন কোনও shared_ptrবস্তুর প্রয়োজন হয় যা তার যে পণ্যটির উপর কল করা হচ্ছে তার মালিকানা পায়, তবে এটি কেবল ফ্লাইতে কোনও বস্তু তৈরি করতে পারে না:

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

এই কোডটির পূর্ববর্তী উদাহরণের মতো একই সমস্যা রয়েছে, যদিও আরও সূক্ষ্ম আকারে। যখন এটি নির্মিত হয়, shared_ptআর অবজেক্টটি sp1সদ্য বরাদ্দকৃত সংস্থার মালিকানায়। কোড সদস্য ফাংশন ভিতরে S::dangerousযে কথা কে না জানে shared_ptrবস্তু, তাই shared_ptrবস্তুর এটি ফেরৎ থেকে স্বতন্ত্র sp1। নতুন shared_ptrঅবজেক্টটি অনুলিপি করা sp2সাহায্য করে না; যখন sp2সুযোগের বাইরে চলে যায়, এটি সংস্থানটি প্রকাশ করবে এবং যখন sp1সুযোগের বাইরে চলে যাবে তখন এটি পুনরায় সংস্থানটি প্রকাশ করবে।

এই সমস্যাটি এড়ানোর উপায় শ্রেণীর টেম্পলেটটি ব্যবহার করা enable_shared_from_this। টেমপ্লেটটি একটি টেম্পলেট ধরণের আর্গুমেন্ট গ্রহণ করে, এটি শ্রেণীবদ্ধের নাম যা পরিচালিত সংস্থানটিকে সংজ্ঞায়িত করে। সেই শ্রেণিকে অবশ্যই পরিবর্তে টেম্পলেট থেকে প্রকাশ্যে নেওয়া উচিত; এটার মত:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

আপনি যখন এটি করেন, মনে রাখবেন যে আপনি যে বস্তুর উপরে কল করবেন তা shared_from_thisঅবশ্যই কোনও shared_ptrঅবজেক্টের মালিকানাধীন । এটি কাজ করবে না:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}

15
ধন্যবাদ, এটি বর্তমানে গৃহীত উত্তরের চেয়ে সমস্যার আরও ভাল সমাধানের চিত্রিত করে।
goertzenator

2
+1: ভাল উত্তর। একপাশে, পরিবর্তে shared_ptr<S> sp1(new S);এটির পক্ষে পছন্দ করা যেতে পারে shared_ptr<S> sp1 = make_shared<S>();, উদাহরণস্বরূপ দেখুন stackoverflow.com/questions/18301511/…
অরুণ

4
আমি পুরোপুরি নিশ্চিত যে শেষ পংক্তিটি পড়া উচিত shared_ptr<S> sp2 = p->not_dangerous();কারণ এখানে সমস্যাটি হ'ল shared_from_this()প্রথমবার কল করার আগে আপনাকে অবশ্যই একটি শেয়ারড_পিটার তৈরি করতে হবে ! এটি ভুল করা সত্যিই সহজ! সি ++ 17 আগে এটা UB ডাকতে shared_from_this(): সামনে ঠিক একটি shared_ptr স্বাভাবিক ভাবেই তৈরি করা হয়েছে auto sptr = std::make_shared<S>();বা shared_ptr<S> sptr(new S());। ধন্যবাদ C ++ 17 এর পরে এমনটি করা ছুঁড়ে যাবে।
আনোরজাকেন


2
পছন্দ করুন আপনি যদি এই সংশোধন করার জন্য কোনও সম্পাদনা অনুরোধ জমা দেন তবে এটি কার্যকর হত। আমি সবেমাত্র তাই করেছি। অন্য দরকারী জিনিস পোস্টারের পক্ষে বিষয়গত, প্রসঙ্গে-সংবেদনশীল পদ্ধতির নামগুলি না বেছে নেওয়া উচিত!
আন্ডারস্কোর_ড ই

30

বাদাম এবং বল্টির দৃষ্টিকোণ থেকে আমার ব্যাখ্যা এখানে রয়েছে (শীর্ষ উত্তর আমার সাথে 'ক্লিক করেনি')। * মনে রাখবেন যে এটি ভাগ করা_পিটার এবং সক্ষম_শ্রেড_ফ্রম_সটি জন্য ভিজ্যুয়াল স্টুডিও ২০১২ এর সাথে উত্স অনুসন্ধানের ফলাফল Perhaps সম্ভবত অন্যান্য সংকলকগণ সক্ষম_শ্রেড_ফর্ম_এটি ভিন্নভাবে প্রয়োগ করে ... *

enable_shared_from_this<T>weak_ptr<T>উদাহরণস্বরূপ T' একটি সত্য রেফারেন্স গণনা ' ধারণ করে এমন একটি ব্যক্তিগত উদাহরণ যুক্ত করে T

সুতরাং, আপনি যখন প্রথম shared_ptr<T>কোনও নতুন টি * তে একটি তৈরি করেন, তখন টি-র অভ্যন্তরীণ দুর্বল_পিটারটি 1 এর পুনঃনিরোধক দিয়ে সূচনা হয় The নতুন shared_ptrমূলত এটির পিছনে থাকে weak_ptr

Tতারপরে, তার পদ্ধতিগুলিতে, একই অভ্যন্তরীণভাবে সঞ্চিত রেফারেন্স গণনায় সেই ব্যাকগুলিরshared_from_this একটি উদাহরণ পাওয়ার জন্য কল করতে পারে । এইভাবে, আপনার সর্বদা এক জায়গায় থাকে যেখানে একে অপরের সম্পর্কে না জেনে একাধিক উদাহরণ থাকার চেয়ে রেফ-কাউন্ট সংরক্ষণ করা হয় এবং প্রত্যেকে মনে করেন যে তারা তারাই রেফ গণনা এবং মুছে ফেলার দায়িত্বে আছে যখন তাদের রেফ হিসাব শূন্যে পৌঁছেছে।shared_ptr<T>T*shared_ptrshared_ptrT


1
এটি সঠিক, এবং সত্যিকারের গুরুত্বপূর্ণ অংশটি So, when you first create...কারণ এটি একটি প্রয়োজনীয়তা (যেমন আপনি বলছেন দুর্বল_সিপিটি আরম্ভ করা হয় না যতক্ষণ না আপনি অবজেক্ট পয়েন্টারকে একটি শেয়ার্ড_সিপিটি কর্টারে পাস না করেন!) এবং এই প্রয়োজনীয়তাটি যদি আপনি হন তবে জিনিসগুলি মারাত্মকভাবে ভুল হতে পারে where সাবধান না আপনি shared_from_thisইউবি পাওয়ার আগে কল করার আগে যদি আপনি কোনও শেয়ারড_পিটার না তৈরি করেন - একইভাবে আপনি যদি একাধিক শেয়ারড_পিটার তৈরি করেন তবে আপনিও ইউবি পাবেন। আপনি একরকম নিশ্চিত করুন যে আপনি একটি shared_ptr তৈরি করতে হবে ঠিক একবার।
আনোরজাকেন

2
অন্য কথায় সম্পূর্ণ ধারণাটি enable_shared_from_thisভঙ্গুর, যেহেতু বিন্দুটি একটি shared_ptr<T>থেকে একটি পেতে সক্ষম হতে পারে T*তবে বাস্তবে আপনি যখন একটি পয়েন্টার T* tপান তবে এটি ইতিমধ্যে ভাগ হয়ে গেছে বা না সে সম্পর্কে কিছুই অনুমান করা নিরাপদ নয় এবং ভুল অনুমান করা ইউবি হয়।
আনোরজাকেন

" অভ্যন্তরীণ দুর্বল_পিটিআর 1 টির একটি পুনরায় গণনা দিয়ে সূচনা হয় " টি থেকে দুর্বল পিটিআর টি হ'ল স্মার্ট পিটিআর মালিকানাধীন A একটি দুর্বল পিটিআর একটি নিজস্ব পিআরটি তৈরির জন্য পর্যাপ্ত তথ্যের একটি নিজস্ব স্মার্ট রেফ যা অন্য নিজস্ব পিআরটির একটি "অনুলিপি"। একটি দুর্বল পিটিআরটির কোনও রেফ গণনা নেই। এটিতে সমস্ত মালিকানার রেফের মতো একটি রেফ কাউন্টে অ্যাক্সেস রয়েছে।
কৌতূহলী

3

নোট করুন যে একটি বুস্ট :: ইন্টুসিভ_পিটার ব্যবহার করে এই সমস্যাটি ভুগছে না। এটি প্রায়শই এই সমস্যাটি ঘুরে দেখার আরও বেশি সুবিধাজনক উপায়।


হ্যাঁ, তবে enable_shared_from_thisআপনাকে এমন একটি এপিআই দিয়ে কাজ করতে দেয় যা বিশেষত গ্রহণ করে shared_ptr<>। আমার মতে, এই জাতীয় একটি এপিআই হ'ল সাধারণত এটি করা ভুল হয় (কারণ স্ট্যাকের মধ্যে কিছু উচ্চতর মেমরির মালিকানা দেওয়া ভাল) তবে আপনি যদি এই জাতীয় কোনও এপিআইয়ের সাথে কাজ করতে বাধ্য হন তবে এটি একটি ভাল বিকল্প।
cdunn2001

2
যতটা সম্ভব স্ট্যান্ডার্ডের মধ্যে থাকা ভাল।
সের্গেই

3

এটি সি ++ 11 এবং পরবর্তী ক্ষেত্রে ঠিক একই: এটি আপনাকে একটি কাঁচা পয়েন্টার দেয় বলে thisভাগ করে নেওয়া পয়েন্টার হিসাবে ফিরে আসার ক্ষমতা সক্ষম করে this

অন্য কথায়, এটি আপনাকে এই জাতীয় কোড ঘুরিয়ে দেওয়ার অনুমতি দেয়

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

এটিতে:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           

এটি কেবল তখনই কাজ করবে যদি এই বিষয়গুলি সর্বদা একটি দ্বারা পরিচালিত হয় shared_ptr। আপনি ইন্টারফেসটি পরিবর্তন করতে চান এটি নিশ্চিত হওয়ার জন্য।
কৌতূহলী

1
আপনি সম্পূর্ণরূপে @Curiousguy are এটি না বলে চলে যায়। আমি আমার পাবলিক এপিআইগুলি সংজ্ঞায়িত করার সময় পাঠযোগ্যতা উন্নত করতে আমার সমস্ত ভাগ করা_পিটারকে টাইডেফ-ইনগ করতে চাই। উদাহরণস্বরূপ, পরিবর্তে std::shared_ptr<Node> getParent const(), আমি সাধারণত এটির NodePtr getParent const()পরিবর্তে প্রকাশ করব । আপনার যদি অভ্যন্তরীণ কাঁচা পয়েন্টারটিতে একেবারে অ্যাক্সেসের প্রয়োজন হয় (সেরা উদাহরণ: একটি সি লাইব্রেরির সাথে ডিল করা), রয়েছেstd::shared_ptr<T>::get জন্য এটি রয়েছে, যা উল্লেখ করা আমি ঘৃণা করি কারণ এই কাঁচা পয়েন্টার অ্যাক্সেসরটি ভুল কারণে অনেকবার ব্যবহার করেছি।
mchiasson

-3

আরেকটি উপায় হ'ল একটি weak_ptr<Y> m_stubসদস্যকে যুক্ত করুন class Y। তারপর লিখ:

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

আপনি যখন ক্লাসটি সংগ্রহ করছেন তা পরিবর্তন করতে পারবেন না (যেমন অন্যান্য ব্যক্তির লাইব্রেরি প্রসারিত) যখন দরকারী। সদস্যকে সূচনা করতে ভুলবেন না, যেমন দ্বারাm_stub = shared_ptr<Y>(this) , এটি কোনও নির্মাণকারীর সময়ও বৈধ।

উত্তরাধিকার শ্রেণিবিন্যাসে যদি এর মতো আরও কিছু স্টাব থাকে তবে এটি অবজেক্টের ধ্বংসকে আটকাবে না OK

সম্পাদনা: ব্যবহারকারী নোবার দ্বারা সঠিকভাবে নির্দেশিত হিসাবে, অ্যাসাইনমেন্টটি শেষ হয়ে গেলে এবং অস্থায়ী ভেরিয়েবলগুলি নষ্ট হয়ে গেলে কোডটি ওয়াই অবজেক্টটিকে ধ্বংস করবে। সুতরাং আমার উত্তরটি ভুল।


4
যদি এখানে আপনার উদ্দেশ্যটি এমন একটি উত্পাদন করা হয় shared_ptr<>যা এর পয়েন্টটি মুছে না, তবে এটি ওভারকিল। আপনি কেবল বলতে পারেন return shared_ptr<Y>(this, no_op_deleter);যেখানে no_op_deleterঅযাচিত ফাংশন অবজেক্টটি Y*কিছুই নিচ্ছে না এবং করছে।
জন জুইঙ্ক

2
এটি কার্যক্ষম সমাধান বলে অসম্ভব বলে মনে হচ্ছে। m_stub = shared_ptr<Y>(this)এটি থেকে অস্থায়ী শেয়ার্ড_পিটারটি নির্মাণ এবং তত্ক্ষণাত ধ্বংস করে দেবে। এই বিবৃতিটি শেষ হয়ে গেলে, thisমুছে ফেলা হবে এবং পরবর্তী সমস্ত রেফারেন্সগুলি জটলা হবে।
নোবার

2
লেখক এই উত্তরটি ভুল বলে স্বীকার করেছেন যাতে তিনি সম্ভবত এটি মুছতে পারেন। কিন্তু তিনি সর্বশেষে 4.5 বছরে লগইন করেছিলেন তাই এরকম হওয়ার সম্ভাবনা নেই - উচ্চতর ক্ষমতা সম্পন্ন কেউ কি এই লাল রঙের হারিংটি সরিয়ে ফেলতে পারে?
টম গুডফেলো

যদি আপনি এর বাস্তবায়নের দিকে লক্ষ্য করেন enable_shared_from_this, এটি weak_ptrনিজের মধ্যে একটি রাখে (কর্টারের দ্বারা জনবহুল), shared_ptrযখন আপনি কল করবেন তখন ফিরে আসবে shared_from_this। অন্য কথায়, আপনি enable_shared_from_thisইতিমধ্যে সরবরাহ করে যা সদৃশ হয় ।
mchiasson
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.