কেবল সুরক্ষিত বা বেসরকারী নির্মাণকারীর সাথে ক্লাসে আমি কীভাবে :: std :: Make_shared কল করব?


187

আমার এই কোডটি কার্যকর হয় না তবে আমি মনে করি উদ্দেশ্যটি পরিষ্কার:

testmakeshared.cpp

#include <memory>

class A {
 public:
   static ::std::shared_ptr<A> create() {
      return ::std::make_shared<A>();
   }

 protected:
   A() {}
   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

::std::shared_ptr<A> foo()
{
   return A::create();
}

আমি এটি সংকলন করার সময় আমি এই ত্রুটিটি পেয়েছি:

g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0,
                 from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86,
                 from testmakeshared.cpp:1:
testmakeshared.cpp: In constructor std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8:   instantiated from std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35:   instantiated from std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64:   instantiated from std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39:   instantiated from std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42:   instantiated from std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]’
testmakeshared.cpp:6:40:   instantiated from here
testmakeshared.cpp:10:8: error: A::A()’ is protected
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context

Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58

এই বার্তাটি মূলত বলে দিচ্ছে যে টেম্পলেট ইনস্ট্যান্টেশন স্ট্যাকের কিছু এলোমেলো পদ্ধতি অবধি ::std::make_sharedনির্মাণকারীর অ্যাক্সেস করতে পারে না কারণ এটি সুরক্ষিত।

তবে আমি সত্যিই উভয়ই ব্যবহার করতে চাই ::std::make_sharedএবং এই শ্রেণীর একটি বিষয় তৈরি করতে কাউকে বাধা দিতে চাই যা একটি দ্বারা নির্দেশিত নয় ::std::shared_ptr। এই কাজ করা সম্ভব কোনো উপায় আছে কি?


আপনি সেই ফাংশনটি গভীরভাবে চিহ্নিত করতে পারেন যা নির্মাণকারীর বন্ধু হিসাবে প্রয়োজন তবে এটি পোর্টেবল হবে না।
দানি

@ দানি: হ্যাঁ, একটি বহনযোগ্য সমাধান পেয়ে ভাল লাগবে। কিন্তু এটি কাজ করবে।
সর্বজনীন

উত্তর:


109

এই উত্তর সম্ভবত ভাল, এবং আমি সম্ভবত এটি গ্রহণ করব। তবে আমি এমন একটি পদ্ধতিও নিয়ে এসেছি যা কৃপণকর, তবে এখনও সবকিছুকে ইনলাইন হতে দেয় এবং উত্পন্ন শ্রেণীর প্রয়োজন হয় না:

#include <memory>
#include <string>

class A {
 protected:
   struct this_is_private;

 public:
   explicit A(const this_is_private &) {}
   A(const this_is_private &, ::std::string, int) {}

   template <typename... T>
   static ::std::shared_ptr<A> create(T &&...args) {
      return ::std::make_shared<A>(this_is_private{0},
                                   ::std::forward<T>(args)...);
   }

 protected:
   struct this_is_private {
       explicit this_is_private(int) {}
   };

   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

::std::shared_ptr<A> foo()
{
   return A::create();
}

::std::shared_ptr<A> bar()
{
   return A::create("George", 5);
}

::std::shared_ptr<A> errors()
{
   ::std::shared_ptr<A> retval;

   // Each of these assignments to retval properly generates errors.
   retval = A::create("George");
   retval = new A(A::this_is_private{0});
   return ::std::move(retval);
}

2017-01-06 সম্পাদনা করুন: আমি এটিকে পরিবর্তন করে পরিষ্কার করেছিলাম যে এই ধারণাটি নির্মাতাদের পক্ষে পরিষ্কার এবং সহজভাবে বর্ধনযোগ্য যা আর্গুমেন্ট গ্রহণ করে কারণ অন্যান্য ব্যক্তিরা lines লাইনগুলির সাথে উত্তর সরবরাহ করছিল এবং এ সম্পর্কে বিভ্রান্ত বলে মনে হয়েছিল।


14
বাস্তবিক, আমি ঐ অর্থহীন শুধুমাত্র হিসাবে ব্যবহার স্ট্রাকচার বিপুল ভক্ত কী । আমি এটিকে লুসের সমাধানের পক্ষে পছন্দ করি তবে উত্তরাধিকারের বিপরীতে এটি আমার বাইসিস হতে পারে।
ম্যাথিউ এম।

2
সম্মত, আমি এটি আরও ভাল পছন্দ করি।
iljarn

3
@ বার্কাস: তারপরে এটি protectedতৈরি করুন private। এবং "এটি" দ্বারা, আমি this_is_privateক্লাসের কথা উল্লেখ করছি , যা সম্ভবত এই জাতীয় নাম পরিবর্তন করা উচিত। আমি সাধারণত constructor_accessআমার কোড এ কল ।
ডালে

1
দুর্ভাগ্যক্রমে এটি কাজ করে না যদি আপনার নির্মাণকারী প্রকৃত পরামিতি নেয়; এই ক্ষেত্রে আপনি {}প্রকারের নামটি অ্যাক্সেস না করেই কেবল প্রাইভেট ট্যাগের জন্য পাস করতে পারেন (জি ++ ৪.৯.০ দিয়ে পরীক্ষিত)। সত্যিকারের পরামিতি ছাড়াই এটি Afrom} থেকে তৈরির চেষ্টা করে , যদিও আমি জানি না কেন এবং ব্যর্থ। আমি মনে করি এই_আইস_প্রাইভেট কনস্ট্রাক্টরকে বেসরকারী করে তোলা এবং এটি তৈরির জন্য একটি স্থিতিশীল পদ্ধতি সরবরাহ করা এটি ঠিক করে দেয়, কারণ আপনি কোনও সদস্য ফাংশনের স্বাক্ষরে টাইপটি ফাঁস না করে বাইরে থেকে এই পদ্ধতিতে অ্যাক্সেস করার কোনও উপায় থাকতে হবে না।
স্টেফান

3
স্টিফান, আপনি যদি this_is_privateএকটি প্রাইভেট সিটার দেন তবে আপনি ক্লাস এটিকে বন্ধু বানিয়ে নিতে পারেন। লুফোলটি বন্ধ বলে মনে হচ্ছে।
স্টিভেন ক্র্যামার

78

std::make_shared20.7.2.2.6 এ ভাগ করা_পিটার তৈরির জন্য প্রয়োজনীয় ব্যবহারকারীর দিকে নজর রাখছেন [Use.smartptr.shared.create], অনুচ্ছেদ 1:

প্রয়োজন: অভিব্যক্তি ::new (pv) T(std::forward<Args>(args)...), যেখানে pvটাইপ হয়েছে void*এবং প্রকারের একটি বস্তু রাখা উপযুক্ত স্টোরেজে পয়েন্ট T, ভাল গঠিত হইবে। Aবরাদ্দকারী হতে হবে (17.6.3.5) .5 এর অনুলিপি নির্মাণকারী এবং ডেস্ট্রাক্টর Aব্যতিক্রম নিক্ষেপ করবে না।

যেহেতু প্রয়োজনটি শর্তহীনভাবে সেই অভিব্যক্তির দিক দিয়ে নির্দিষ্ট করা হয়েছে এবং সুযোগের মতো জিনিসগুলিকে বিবেচনা করা হয় না, তাই আমি মনে করি বন্ধুত্বের মতো কৌশলগুলি ঠিক আছে are

একটি সহজ সমাধান থেকে প্রাপ্ত A। এর জন্য Aইন্টারফেস বা এমনকি পলিমারফিক ধরণের তৈরি করার প্রয়োজন নেই।

// interface in header
std::shared_ptr<A> make_a();

// implementation in source
namespace {

struct concrete_A: public A {};

} // namespace

std::shared_ptr<A>
make_a()
{
    return std::make_shared<concrete_A>();
}

1
ওহ, এটি একটি খুব চালাক উত্তর, এবং সম্ভবত আমি যেটা ভাবলাম তার চেয়ে ভাল।
সর্বজনীন

যদিও একটি প্রশ্ন, ভাগ করা_পিটার একটি ক না একটি কংক্রিট_এ মুছে ফেলবে, এবং এটি সমস্যার কারণ হতে পারে না?
সর্বজনীন

8
আহ্, এটি কারণ shared_ptrইনস্ট্যান্টেশনের সময় একটি মুছক সংরক্ষণ করে এবং আপনি যদি make_sharedডিল্টারটি ব্যবহার করেন তবে একেবারে সঠিক প্রকারটি ব্যবহার করতে হবে।
সর্বজনীন

1
@ লুকড্যান্টন প্রশ্ন ইন্টারফেস সম্পর্কে নয়, শিরোনামের পরামর্শ অনুসারে তিনি একটি প্রাইভেট সিটারও চাইছেন। এছাড়াও, যে কারণে আমি এই প্রশ্নে আছি। কিছু পুরানো কোড ম্যাকিয়াভেলি ক্লাস রয়েছে, যার একটি বেসরকারী কর্টর রয়েছে এবং একটি কাঁচা পয়েন্টার ফিরিয়ে দেওয়ার পদ্ধতি রয়েছে এবং আমি সেগুলি স্মার্ট পয়েন্টারে রূপান্তরিত করার চেষ্টা করছি।
জহির

2
আমি এই পদ্ধতির পছন্দ করি (এটি নিজেই ব্যবহার করে) তবে আপনার ভার্চুয়াল ডেস্ট্রাক্টর দরকার। এটি আর্গুমেন্ট সহ কনস্ট্রাক্টরগুলিতে ভাল প্রসারিত করে (কেবল একটি পাসথ্রু কনস্ট্রাক্টর সরবরাহ করে)। এবং আপনি যদি ব্যক্তিগতের চেয়ে সুরক্ষিত ব্যবহার করে থাকেন তবে আপনি এটিকে শিরোলেখের ব্যবহারকারীদের কাছে সম্পূর্ণ অদৃশ্য করে তুলতে পারেন।
জো স্টিলে

69

সম্ভবত সহজ সমাধান। মোহিত আরন এর পূর্ববর্তী উত্তরের ভিত্তিতে এবং ডিএলএফ এর পরামর্শকে অন্তর্ভুক্ত করে।

#include <memory>

class A
{
public:
    static std::shared_ptr<A> create()
    {
        struct make_shared_enabler : public A {};

        return std::make_shared<make_shared_enabler>();
    }

private:
    A() {}  
};

5
যদি Aআপনি তাদের প্রভাবাধীন করতে হবে অ ডিফল্ট কনস্ট্রাকটর রয়েছে: struct make_shared_enabler : public A { template <typename... Args> make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...) {} };। এটি সকল প্রাইভেট কনস্ট্রাক্টরকে কনস্ট্রাক্টর হিসাবে Aদৃশ্যমান করে তোলে make_shared_enabler। কনস্ট্রাক্টরদের উত্তরাধিকার বৈশিষ্ট্য ( using A::A;) ব্যবহার করা এখানে সহায়তা করে না বলে মনে হয় কারণ নির্মাণকারীরা এখনও ব্যক্তিগত।
অ্যান্টন_আরহ

2
@ অ্যান্টন_আরহ: আপনি অভ্যন্তরীণ ক্লাসে টেম্পলেট যুক্তি যুক্ত করতে পারবেন না। এখানে দেখুন ।
বকবেল

3
হুম ... মনে হয় আপনি ঠিক বলেছেন। আমার ক্ষেত্রে struct হয় স্থানীয় ছিলেন না কিন্তু একটি বেসরকারি struct হয় ছিল: class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }। এখানে দেখুন সিপিপি.এস / q65 কিউবিআর
anton_rh

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

30

এর জন্য একটি পরিষ্কার সমাধান এখানে দেওয়া হয়েছে:

#include <memory>

class A {
   public:
     static shared_ptr<A> Create();

   private:
     A() {}

     struct MakeSharedEnabler;   
 };

struct A::MakeSharedEnabler : public A {
    MakeSharedEnabler() : A() {
    }
};

shared_ptr<A> A::Create() {
    return make_shared<MakeSharedEnabler>();
}

3
আমি এই পছন্দ। MakeSharedEnablerস্থানীয়ভাবে ভিতরে ভিতরে সংজ্ঞা দিয়ে এটিকে কিছুটা সহজ করা যায় A::Create()
dlf

অসাধারণ ধারণা মোহিত এটি আমাকে অনেক সাহায্য করেছিল।
জান্না

12

এ কেমন?

static std::shared_ptr<A> create()
{
    std::shared_ptr<A> pA(new A());
    return pA;
}

13
এটি দুর্দান্ত কাজ করে। তবে ::std::make_sharedকেবল কিছুতে একটি শেয়ারড_পিটার তৈরির উপরে এবং তার বাইরে কার্যকারিতা রয়েছে। এটি বস্তুর সাথে রেফারেন্স গণনাও বরাদ্দ করে যাতে তারা একে অপরের কাছাকাছি অবস্থিত। আমি সত্যিই, সত্যিই ব্যবহার করতে চান ::std::make_shared
সর্বজনীন

মুছে ফেলা দায়বদ্ধতা এবং অনুলিপি অপারেটররা এটি নিষিদ্ধ করে
দানি

7
এটি সত্যিই সবচেয়ে সরল পদ্ধতি, যদিও প্রশ্নটি যা জিজ্ঞাসা করেছিল তা বাস্তবে নয়। মেক_শ্যাডারের কিছু দুর্দান্ত বৈশিষ্ট্য রয়েছে এবং আমি এটি যেখানেই সম্ভব ব্যবহার করার চেষ্টা করি তবে এই পরিস্থিতিতে এটি সম্ভবত সম্ভবত মনে হয় যে মেক_শায়ারডের রান-টাইম পারফরম্যান্স সুবিধাগুলি এটি ব্যবহারের জন্য প্রয়োজনীয় অতিরিক্ত কোড জটিলতা এবং অনুষ্ঠানের চেয়ে বেশি নয়। আপনার যদি সত্যিই মেক_শ্যাডারের পারফরম্যান্সের প্রয়োজন হয় তবে পাগল হয়ে যান, তবে কেবল শেয়ারড_প্ট্রের কনস্ট্রাক্টর ব্যবহারের সরলতাকে উপেক্ষা করবেন না।
কেভিন

মেমরি তথ্য ফাঁসের বিষয়ে সতর্ক হন যদিও ... দেখুন এই প্রশ্নের stackoverflow.com/a/14837300/2149539
dgmz

12
struct A {
public:
  template<typename ...Arg> std::shared_ptr<A> static create(Arg&&...arg) {
    struct EnableMakeShared : public A {
      EnableMakeShared(Arg&&...arg) :A(std::forward<Arg>(arg)...) {}
    };
    return std::make_shared<EnableMakeShared>(std::forward<Arg>(arg)...);
  }
  void dump() const {
    std::cout << a_ << std::endl;
  }
private:
  A(int a) : a_(a) {}
  A(int i, int j) : a_(i + j) {}
  A(std::string const& a) : a_(a.size()) {}
  int a_;
};

এটি লুস ড্যান্টনের উত্তরের মতো একই জিনিস, যদিও এটিকে স্থানীয় শ্রেণিতে পরিণত করা একটি দুর্দান্ত স্পর্শ। কোড সহ কিছু ব্যাখ্যা এটি আরও ভাল উত্তর দিতে পারে।

সাধারণত, আমি সিসি ফাইল নয়, শিরোনাম ফাইলে যেমন ছোট ফাংশন লিখতে চাই। দ্বিতীয়ত, অনুশীলনে, আমি একটি ম্যাক্রো ব্যবহার করি যা দেখতে # ডিফাইন SharedPtrCreate (টি) টেমপ্লেট <টাইপনাম ... আরগ> .....
আলফা

ভাল উত্তর. এমনকি আমি এটিকে IMPLEMENT_CREATE_SHARED (ClassName)
ivan.ukr

8

যেহেতু আমি ইতিমধ্যে সরবরাহিত উত্তরগুলি পছন্দ করি না আমি অনুসন্ধান করার সিদ্ধান্ত নিয়েছিলাম এবং এমন একটি সমাধান পেয়েছি যা পূর্ববর্তী উত্তরগুলির মতো জেনেরিক নয় তবে আমি এটি আরও ভাল (টিএম) পছন্দ করি। পূর্ববর্তী ক্ষেত্রে এটি ওমনিফেরিয়াস সরবরাহকারীর চেয়ে খুব সুন্দর নয় তবে এমন অন্যান্য ব্যক্তিও থাকতে পারে যারা এটি পছন্দ করে :)

এটি আমার দ্বারা উদ্ভাবিত হয়নি, তবে এটি জোনাথন ওয়েকেলি (জিসিসি বিকাশকারী) এর ধারণা।

দুর্ভাগ্যক্রমে এটি সমস্ত সংকলকগুলির সাথে কাজ করে না কারণ এটি স্টাড :: বরাদ্দ_ভাগাযুক্ত বাস্তবায়নের একটি ছোট পরিবর্তনের উপর নির্ভর করে। তবে এই পরিবর্তনটি এখন স্ট্যান্ডার্ড লাইব্রেরিগুলির জন্য প্রস্তাবিত আপডেট, তাই এটি ভবিষ্যতে সমস্ত সংকলক দ্বারা সমর্থিত হতে পারে। এটি জিসিসি ৪.7 এ কাজ করে।

সি ++ স্ট্যান্ডার্ড লাইব্রেরি ওয়ার্কিং গ্রুপ পরিবর্তনের অনুরোধটি এখানে: http://lwg.github.com/issues/lwg-active.html#2070

উদাহরণের ব্যবহার সহ জিসিসি প্যাচটি এখানে রয়েছে: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html

সমাধানটি প্রাইভেট কনস্ট্রাক্টরের সাথে ক্লাসের বন্ধু হিসাবে ঘোষণা করা একটি কাস্টম বরাদ্দকারীর সাথে স্টাড :: বরাদ্দ_শক্তি (স্ট্যান্ড :: মেক_শ্রেডের পরিবর্তে) ব্যবহার করার আইডিয়াটিতে কাজ করে।

ওপি থেকে প্রাপ্ত উদাহরণটি এরকম হবে:

#include <memory>

template<typename Private>
struct MyAlloc : std::allocator<Private>
{
    void construct(void* p) { ::new(p) Private(); }
};

class A {
    public:
        static ::std::shared_ptr<A> create() {
            return ::std::allocate_shared<A>(MyAlloc<A>());
        }

    protected:
        A() {}
        A(const A &) = delete;
        const A &operator =(const A &) = delete;

        friend struct MyAlloc<A>;
};

int main() {
    auto p = A::create();
    return 0;
}

আরও জটিল উদাহরণ যা আমি যে ইউটিলিটিটিতে কাজ করছি তার উপর ভিত্তি করে। এটি দিয়ে আমি লুসের সমাধানটি ব্যবহার করতে পারিনি। তবে ওমনিফেরিয়াসের একটিটি মানিয়ে নেওয়া যেতে পারে। পূর্ববর্তী উদাহরণের মধ্যে সবাই যখন এইটিতে মাইএলোক ব্যবহার করে একটি বস্তু তৈরি করতে পারে তখন তৈরি () পদ্ধতি ছাড়াও এ বা বি তৈরি করার উপায় নেই।

#include <memory>

template<typename T>
class safe_enable_shared_from_this : public std::enable_shared_from_this<T>
{
    public:
    template<typename... _Args>
        static ::std::shared_ptr<T> create(_Args&&... p_args) {
            return ::std::allocate_shared<T>(Alloc(), std::forward<_Args>(p_args)...);
        }

    protected:
    struct Alloc : std::allocator<T>
    {  
        template<typename _Up, typename... _Args>
        void construct(_Up* __p, _Args&&... __args)
        { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    };
    safe_enable_shared_from_this(const safe_enable_shared_from_this&) = delete;
    safe_enable_shared_from_this& operator=(const safe_enable_shared_from_this&) = delete;
};

class A : public safe_enable_shared_from_this<A> {
    private:
        A() {}
        friend struct safe_enable_shared_from_this<A>::Alloc;
};

class B : public safe_enable_shared_from_this<B> {
    private:
        B(int v) {}
        friend struct safe_enable_shared_from_this<B>::Alloc;
};

int main() {
    auto a = A::create();
    auto b = B::create(5);
    return 0;
}

6

আদর্শভাবে, আমি মনে করি নিখুঁত সমাধানটির জন্য সি ++ স্ট্যান্ডার্ডের সংযোজন প্রয়োজন। অ্যান্ড্রু শ্যাপলার নিম্নলিখিত প্রস্তাব করেছেন:

( পুরো থ্রেডের জন্য এখানে যান )

আমরা বুস্ট :: পুনরুদ্ধারকারী_কোরি_অ্যাকসেস থেকে একটি ধার ধার নিতে পারি। আমি std::shared_ptr_accessকোনও সার্বজনীন বা সুরক্ষিত সদস্যবিহীন একটি নতুন শ্রেণির প্রস্তাব দিচ্ছি এবং স্ট্যান্ড :: মেক_শ্রেড (আরগস ...) এবং স্টাড :: বরাদ্দ_শ্রেড (ক, আরগস ...), এক্সপ্রেশন :: নতুন (পিভি) টি এর জন্য নির্দিষ্ট করার জন্য (ফরওয়ার্ড (আরগস) ...) এবং পিটিআর-> ~ টি () অবশ্যই স্ট্যান্ডার্ড :: শেয়ারড_পিটার_অ্যাক্সেসের প্রসঙ্গে সুসংহত হতে হবে।

স্টাড :: শেয়ার্ড_পিটার_একসেসের একটি বাস্তবায়ন এর মতো দেখতে পাওয়া যেতে পারে:

namespace std {
    class shared_ptr_access
    {
        template <typename _T, typename ... _Args>
        static _T* __construct(void* __pv, _Args&& ... __args)
        { return ::new(__pv) _T(forward<_Args>(__args)...); }

        template <typename _T>
        static void __destroy(_T* __ptr) { __ptr->~_T(); }

        template <typename _T, typename _A>
        friend class __shared_ptr_storage;
    };
}

ব্যবহার

যদি / যখন উপরের স্ট্যান্ডার্ডটিতে যুক্ত হয়, আমরা কেবল এটি করব:

class A {
public:
   static std::shared_ptr<A> create() {
      return std::make_shared<A>();
   }

 protected:
   friend class std::shared_ptr_access;
   A() {}
   A(const A &) = delete;
   const A &operator =(const A &) = delete;
};

যদি এটি আপনার কাছে মানকে কোনও গুরুত্বপূর্ণ সংযোজন বলে মনে হয় তবে লিঙ্কযুক্ত আইসোকপি গুগল গ্রুপে আপনার 2 সেন্ট যোগ করতে নির্দ্বিধায় মনে করুন।


1
আমি মনে করি এটি স্ট্যান্ডার্ডের জন্য একটি ভাল সংযোজন, তবে গুগল গ্রুপে যোগ দিতে এবং মন্তব্য করতে এবং তারপরে সেই গোষ্ঠীটি এবং মন্তব্যে মনোযোগ দেওয়া আমার পক্ষে যথেষ্ট গুরুত্বপূর্ণ নয়। :-)
সর্বসম্মত

4

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

#include <memory>

#if defined(__cplusplus) && __cplusplus >= 201103L
#define ALLOW_MAKE_SHARED(x) friend void __gnu_cxx::new_allocator<test>::construct<test>(test*);
#elif defined(_WIN32) || defined(WIN32)
#if defined(_MSC_VER) && _MSC_VER >= 1800
#define ALLOW_MAKE_SHARED(x) friend class std::_Ref_count_obj;
#else
#error msc version does not suport c++11
#endif
#else
#error implement for platform
#endif

class test {
    test() {}
    ALLOW_MAKE_SHARED(test);
public:
    static std::shared_ptr<test> create() { return std::make_shared<test>(); }

};
int main() {
    std::shared_ptr<test> t(test::create());
}

আমি উইন্ডোজ এবং লিনাক্সে পরীক্ষা করেছি, এটি বিভিন্ন প্ল্যাটফর্মের জন্য টুইঙ্কের প্রয়োজন হতে পারে।


1
আমি বহনযোগ্যতার অভাবে এটি -1 প্রলোভিত করছি। অন্যান্য উত্তরগুলি (বিশেষত 'কী শ্রেণীর' উত্তর) মোটামুটি মার্জিত এবং অ-বহনযোগ্য উত্তরগুলি খুব কুরুচিপূর্ণ। আপনি অ-বহনযোগ্য উত্তরটি ব্যবহার করবেন এমন কোনও কারণ আমি ভাবতে পারি না। এটি দ্রুত বা এর মতো কিছু নয়।
সর্বময়

@ অ্যামিনিফেরিয়াস এটি প্রকৃতপক্ষে বহনযোগ্য নয় এবং আমি এটির সুপারিশ করব না, তবে আমি বিশ্বাস করি এটি আসলে শব্দার্থগতভাবে সবচেয়ে সঠিক সমাধান। ইন আমার উত্তর , আমি যোগ করার একটি প্রস্তাব এর প্রতি সংযোগ আছে std::shared_ptr_accessমান, যা একটি সহজ এবং পোর্টেবল ভাবে উপরে না করতে সক্ষম হবেন হিসেবে দেখা যেতে পারে।
বোরিস ডালস্টাইন

3

একটি আরও লোমশ এবং আকর্ষণীয় সমস্যা দেখা দেয় যখন আপনি দুটি কঠোরভাবে সম্পর্কিত A এবং B ক্লাস করে যা একসাথে কাজ করে।

বলুন এ হ'ল "মাস্টার ক্লাস" এবং বি এর "ক্রীতদাস"। আপনি যদি কেবল খ এর বি ইনস্ট্যান্টেশনকে সীমাবদ্ধ রাখতে চান, আপনি বি এর কনস্ট্রাক্টরকে ব্যক্তিগত এবং বন্ধু বি এর সাথে এটিকে পছন্দ করবেন

class B
{
public:
    // B your methods...

private:
    B();
    friend class A;
};

দুর্ভাগ্যক্রমে std::make_shared<B>()একটি পদ্ধতি থেকে কল করা Aসংকলকটি B::B()ব্যক্তিগত থাকার বিষয়ে অভিযোগ করবে ।

আমার সমাধানটি হ'ল এর মধ্যে একটি পাবলিক Passডামি ক্লাস তৈরি করা (ঠিক যেমন nullptr_t) এর মধ্যে Bপ্রাইভেট কনস্ট্রাক্টর রয়েছে এবং তার সাথে বন্ধুত্ব হয় Aএবং Bএর কনস্ট্রাক্টরকে পাবলিক করে তোলে এবং এর Passমত যুক্তি যুক্ত করে।

class B
{
public:
  class Pass
  {
    Pass() {}
    friend class A;
  };

  B(Pass, int someArgument)
  {
  }
};

class A
{
public:
  A()
  {
    // This is valid
    auto ptr = std::make_shared<B>(B::Pass(), 42);
  }
};

class C
{
public:
  C()
  {
    // This is not
    auto ptr = std::make_shared<B>(B::Pass(), 42);
  }
};

3

আপনি যদি কোনও কন্ডাক্টরকে আর্গুমেন্ট গ্রহণ করতে সক্ষম করতে চান তবে এটি কিছুটা সহায়তা করতে পারে।

#include <memory>
#include <utility>

template<typename S>
struct enable_make : public S
{
    template<typename... T>
    enable_make(T&&... t)
        : S(std::forward<T>(t)...)
    {
    }
};

class foo
{
public:
    static std::unique_ptr<foo> create(std::unique_ptr<int> u, char const* s)
    {
        return std::make_unique<enable_make<foo>>(std::move(u), s);
    }
protected:
    foo(std::unique_ptr<int> u, char const* s)
    {
    }
};

void test()
{
    auto fp = foo::create(std::make_unique<int>(3), "asdf");
}

3

[সম্পাদনা] আমি একটি মানক std::shared_ptr_access<>প্রস্তাবিত উপরে উল্লিখিত থ্রেডটি পড়েছি । এর মধ্যে একটি প্রতিক্রিয়া ছিল যার সমাধান স্থির করে std::allocate_shared<>এবং এর ব্যবহারের একটি উদাহরণ। আমি এটিকে নীচে একটি কারখানার টেম্পলেটে মানিয়ে নিয়েছি এবং এটি জিসিসি সি ++ 11/14/17 এর অধীনে পরীক্ষা করেছি। এটি std::enable_shared_from_this<>পাশাপাশি কাজ করে , সুতরাং অবশ্যই এই উত্তরটিতে আমার মূল সমাধানের চেয়ে ভাল। এটা এখানে...

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        return std::allocate_shared<T>(Alloc<T>(), std::forward<A>(args)...);
    }
private:
    template<typename T>
    struct Alloc : std::allocator<T> {
        template<typename U, typename... A>
        void construct(U* ptr, A&&... args) {
            new(ptr) U(std::forward<A>(args)...);
        }
        template<typename U>
        void destroy(U* ptr) {
            ptr->~U();
        }
    };  
};

class X final : public std::enable_shared_from_this<X> {
    friend class Factory;
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(int) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto p1 = Factory::make_shared<X>(42);
    auto p2 = p1->shared_from_this();
    std::cout << "p1=" << p1 << "\n"
              << "p2=" << p2 << "\n"
              << "count=" << p1.use_count() << "\n";
}

[মূল] ভাগ করা পয়েন্টার এলিয়জিং কনস্ট্রাক্টর ব্যবহার করে আমি একটি সমাধান পেয়েছি। এটি কর্টর এবং ডটর উভয়কেই বেসরকারী হতে দেয়, পাশাপাশি চূড়ান্ত স্পেসিফায়ার ব্যবহার করে।

#include <iostream>
#include <memory>

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        return std::shared_ptr<T>(ptr, &ptr->type);
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
};

class X final {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = Factory::make_shared<X>(42);
}

নোট করুন যে উপরের পদ্ধতির সাথে ভাল খেলছে না std::enable_shared_from_this<>কারণ প্রাথমিকটি std::shared_ptr<>র‌্যাপারের এবং প্রকারটি নিজেই নয়। আমরা এটি কারখানার সাথে সামঞ্জস্যপূর্ণ সমতুল্য শ্রেণীর সাথে সম্বোধন করতে পারি ...

#include <iostream>
#include <memory>

template<typename T>
class EnableShared {
    friend class Factory;  // factory access
public:
    std::shared_ptr<T> shared_from_this() { return weak.lock(); }
protected:
    EnableShared() = default;
    virtual ~EnableShared() = default;
    EnableShared<T>& operator=(const EnableShared<T>&) { return *this; }  // no slicing
private:
    std::weak_ptr<T> weak;
};

class Factory final {
public:
    template<typename T, typename... A>
    static std::shared_ptr<T> make_shared(A&&... args) {
        auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
        auto alt = std::shared_ptr<T>(ptr, &ptr->type);
        assign(std::is_base_of<EnableShared<T>, T>(), alt);
        return alt;
    }
private:
    template<typename T>
    struct Type final {
        template<typename... A>
        Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
        ~Type() { std::cout << "~Type()\n"; }
        T type;
    };
    template<typename T>
    static void assign(std::true_type, const std::shared_ptr<T>& ptr) {
        ptr->weak = ptr;
    }
    template<typename T>
    static void assign(std::false_type, const std::shared_ptr<T>&) {}
};

class X final : public EnableShared<X> {
    friend struct Factory::Type<X>;  // factory access
private:
    X()      { std::cout << "X() addr=" << this << "\n"; }
    X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
    ~X()     { std::cout << "~X()\n"; }
};

int main() {
    auto ptr1 = Factory::make_shared<X>();
    auto ptr2 = ptr1->shared_from_this();
    std::cout << "ptr1=" << ptr1.get() << "\nptr2=" << ptr2.get() << "\n";
}

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


3

আমারও একই সমস্যা ছিল, তবে সুরক্ষিত কনস্ট্রাক্টরের কাছে আমার যুক্তিগুলি প্রেরণের প্রয়োজন হিসাবে বিদ্যমান উত্তরগুলির কোনওটিই সত্যই সন্তোষজনক নয়। তদুপরি, আমার বেশ কয়েকটি ক্লাসের জন্য এটি করা দরকার, প্রত্যেকে বিভিন্ন যুক্তি গ্রহণ করে।

সেই প্রভাবটির জন্য এবং বিদ্যমান উত্তরগুলির মধ্যে যা সমস্ত একই ধরণের পদ্ধতি ব্যবহার করে সেগুলি তৈরি করে, আমি এই ছোট্ট নকশাকে উপস্থাপন করছি:

template < typename Object, typename... Args >
inline std::shared_ptr< Object >
protected_make_shared( Args&&... args )
{
  struct helper : public Object
  {
    helper( Args&&... args )
      : Object{ std::forward< Args >( args )... }
    {}
  };

  return std::make_shared< helper >( std::forward< Args >( args )... );
}

1

সমস্যার মূলে হ'ল যদি আপনার বন্ধু ফাংশন বা ক্লাসটি আপনার নির্মাণকারীকে নিম্ন স্তরের কল করে তবে তাদেরও বন্ধুত্ব করতে হবে। std :: make_shared এমন কোনও ফাংশন নয় যা প্রকৃতপক্ষে আপনার নির্মাণকারীকে কল করে তাই বন্ধুত্ব করে কোনও তফাত হয় না।

class A;
typedef std::shared_ptr<A> APtr;
class A
{
    template<class T>
    friend class std::_Ref_count_obj;
public:
    APtr create()
    {
        return std::make_shared<A>();
    }
private:
    A()
    {}
};

std :: _ রেফ_কাউন্ট_বজ আসলে আপনার কনস্ট্রাক্টরকে কল করছে, সুতরাং এটির বন্ধু হওয়া দরকার। যেহেতু এটি কিছুটা অস্পষ্ট, আমি ম্যাক্রো ব্যবহার করি

#define SHARED_PTR_DECL(T) \
class T; \
typedef std::shared_ptr<T> ##T##Ptr;

#define FRIEND_STD_MAKE_SHARED \
template<class T> \
friend class std::_Ref_count_obj;

তারপরে আপনার শ্রেণীর ঘোষণাটি মোটামুটি সহজ দেখাচ্ছে। আপনি যদি চান তবে পিটিআর এবং ক্লাস ঘোষণার জন্য একটি একক ম্যাক্রো তৈরি করতে পারেন।

SHARED_PTR_DECL(B);
class B
{
    FRIEND_STD_MAKE_SHARED
public:
    BPtr create()
    {
        return std::make_shared<B>();
    }
private:
    B()
    {}
};

এটি আসলে একটি গুরুত্বপূর্ণ বিষয়। রক্ষণাবেক্ষণযোগ্য, পোর্টেবল কোড তৈরি করার জন্য আপনার যতটা সম্ভব প্রয়োগের আড়াল করা দরকার।

typedef std::shared_ptr<A> APtr;

আপনি কীভাবে আপনার স্মার্ট পয়েন্টারটিকে সামান্য পরিচালনা করছেন তা লুকিয়ে রাখে, আপনাকে অবশ্যই নিজের টাইপডেফটি ব্যবহার করতে হবে তা নিশ্চিত হতে হবে। তবে আপনাকে যদি সর্বদা মেক-শেয়ার্ড ব্যবহার করে একটি তৈরি করতে হয় তবে এটি উদ্দেশ্যকে পরাস্ত করে।

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

প্রায় 30 বছর ধরে এটি করা, আমি যখন বছরগুলি আগে এটি করা হয়েছিল তখন এটি মেরামত করার জন্য সময়, ব্যথা এবং পার্শ্ব প্রতিক্রিয়াগুলিতে আমি একটি বড় দাম দিয়েছি।


2
std::_Ref_count_objএকটি বাস্তবায়ন বিশদ। এর অর্থ এই মুহূর্তে আপনার প্ল্যাটফর্মে এই সমাধানটি আপনার পক্ষে কার্যকর হতে পারে। তবে এটি অন্যের পক্ষে কাজ না করে এবং আপনার সংকলক আপডেটগুলি যে কোনও সময় কাজ বন্ধ করতে পারে বা আপনি কেবল সংকলন পতাকাগুলি পরিবর্তন করলেও।
ফ্রানসোয়া অ্যান্ডরিয়াক

-3

আপনি এটি ব্যবহার করতে পারেন:

class CVal
{
    friend std::shared_ptr<CVal>;
    friend std::_Ref_count<CVal>;
public:
    static shared_ptr<CVal> create()
    {
        shared_ptr<CVal> ret_sCVal(new CVal());
        return ret_sCVal;
    }

protected:
    CVal() {};
    ~CVal() {};
};

1
ব্যবহার করবেন না std::make_shared
ব্রায়ান

-3
#include <iostream>
#include <memory>

class A : public std::enable_shared_from_this<A>
{
private:
    A(){}
    explicit A(int a):m_a(a){}
public:
    template <typename... Args>
    static std::shared_ptr<A> create(Args &&... args)
    {
        class make_shared_enabler : public A
        {
        public:
            make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...){}
        };
        return std::make_shared<make_shared_enabler>(std::forward<Args>(args)...);
    }

    int val() const
    {
        return m_a;
    }
private:
    int m_a=0;
};

int main(int, char **)
{
    std::shared_ptr<A> a0=A::create();
    std::shared_ptr<A> a1=A::create(10);
    std::cout << a0->val() << " " << a1->val() << std::endl;
    return 0;
}

এই মাত্র এই উত্তরটি সদৃশ হল: stackoverflow.com/a/27832765/167958
সর্ববিধ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.