मैं कैसे कॉल करूं :: std :: make_sared एक वर्ग पर केवल संरक्षित या निजी निर्माणकर्ताओं के साथ?


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 को संपादित करें: मैंने इसे स्पष्ट करने के लिए इसे बदल दिया है कि यह विचार स्पष्ट रूप से और बस उन कंस्ट्रक्टरों के लिए एक्स्टेंसिबल है जो तर्क लेते हैं क्योंकि अन्य लोग उन पंक्तियों के साथ उत्तर प्रदान कर रहे थे और इस बारे में भ्रमित लग रहे थे।


14
वास्तव में, मैं उन अर्थहीन संरचनाओं का बहुत बड़ा प्रशंसक हूं जिनका उपयोग केवल चाबियों के रूप में किया जाता है । मैं इसे ल्यूक के समाधान के लिए पसंद करता हूं, लेकिन हो सकता है कि यह मेरी विरासत के खिलाफ हो।
Matthieu M.

2
सहमत, मुझे यह भी अच्छा लगता है।

3
@Berkus: तो फिर इसे बनाने protectedके बजाय private। और "यह" करके, मैं उस this_is_privateवर्ग का उल्लेख कर रहा हूं , जिसे शायद ऐसे मामले में नाम दिया जाना चाहिए। मैं आमतौर पर इसे constructor_accessअपने कोड में कहता हूं।
दिनांक

1
अफसोस की बात है कि यह काम नहीं करता है यदि आपका निर्माता वास्तविक पैरामीटर लेता है; इस मामले में आप केवल {}निजी टैग के लिए टाइप नाम (जी ++ 4.9.0 के साथ परीक्षण) तक पहुंच के बिना पास कर सकते हैं। वास्तविक मापदंडों के बिना यह A{} से निर्माण करने की कोशिश करता है , हालांकि मुझे नहीं पता कि क्यों, और विफल रहता है। मुझे लगता है कि यह_प्राकृतिक कंस्ट्रक्टर निजी बना रहा है और इसे बनाने के लिए एक स्थिर विधि प्रदान करता है, क्योंकि यह इस विधि को बाहर से एक्सेस करने का कोई तरीका नहीं होना चाहिए, जब तक कि आप सदस्य फ़ंक्शन हस्ताक्षर में प्रकार लीक न करें।
स्टीफन

3
स्टीफन, यदि आप this_is_privateएक निजी ctor देते हैं तो आप कक्षा A को मित्र बना सकते हैं। खामियों को दूर करने के लिए लगता है।
स्टीवन क्रेमर

78

std::make_shared२०..2.२.२.६ साझा_प्रतिमा निर्माण के लिए आवश्यकताओं को देखते हुए [use.smartptr.soded.create], पैरा १:

आवश्यकता है: अभिव्यक्ति ::new (pv) T(std::forward<Args>(args)...), जहाँ pvप्रकार void*और एक प्रकार की वस्तु रखने के लिए उपयुक्त भंडारण के लिए अंक हैं T, अच्छी तरह से बनाया जाएगा। Aएक आवंटनकर्ता (17.6.3.5) होगा। कॉपी कंस्ट्रक्टर और विध्वंसक Aअपवाद नहीं फेंकेंगे।

चूँकि आवश्यकता उस अभिव्यक्ति के संदर्भ में बिना शर्त के निर्दिष्ट है और गुंजाइश जैसी चीजों पर ध्यान नहीं दिया जाता है, मुझे लगता है कि दोस्ती जैसे ट्रिक सही हैं।

एक सरल समाधान से प्राप्त करना है 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
ओह, यह एक बहुत ही चतुर जवाब है, और संभवतः एक और एक से बेहतर है जिसके बारे में मैंने सोचा था।
सर्वग्राही

हालांकि एक सवाल यह है कि share_ptr एक ए को नहीं हटाएगा और न ही एक ठोस_ ए, और यह समस्या पैदा कर सकता है?
सर्वग्राही

8
आह, यह इसलिए है क्योंकि shared_ptrतात्कालिकता के समय एक डीलेटर को स्टोर किया जाता है, और यदि आप make_sharedडीलेटर का उपयोग कर रहे हैं तो सही प्रकार का उपयोग करना होगा।
सर्वग्राही

1
@ ल्यूकैंटन प्रश्न इंटरफेस के बारे में नहीं है, जैसा कि शीर्षक से पता चलता है कि वह एक निजी ctor के लिए भी पूछ रहा है। इसके अलावा, यही कारण है कि मैं इस सवाल पर वैसे भी हूँ। माचियावेली क्लासेस वाले कुछ पुराने कोड, जिनमें एक निजी ctor और एक क्रिएट पॉइंटर को लौटाने की एक विधि है, और मैं उन्हें स्मार्ट पॉइंटर्स में बदलने की कोशिश कर रहा हूं।
ज़ाहिर

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;) का उपयोग करने से लगता है कि यहाँ मदद नहीं मिलती है क्योंकि कंस्ट्रक्टर अभी भी निजी होंगे।
anton_rh

2
@anton_rh: आप आंतरिक कक्षाओं में टेम्पलेट तर्क नहीं जोड़ सकते। देखें यहाँ
बोबेल

3
हम्म ... लगता है आप सही हैं। मेरे मामले में संरचना स्थानीय नहीं थी, लेकिन एक निजी संरचना थी class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }:। यहाँ देखें cpp.sh/65qbr
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()
डीएलएफ

बहुत बढ़िया विचार मोहित ने मुझे बहुत मदद की।
ज्ञान

12

इस बारे में कैसा है?

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

13
यह बहुत अच्छा काम करता है। लेकिन ::std::make_sharedऊपर और परे कार्यक्षमता है बस कुछ करने के लिए एक साझा करने के लिए। यह ऑब्जेक्ट के साथ संदर्भ गणना आवंटित करता है ताकि वे एक दूसरे के करीब स्थित हों। मैं वास्तव में, वास्तव में उपयोग करना चाहता हूं ::std::make_shared
सर्वग्राही

हटाए गए एसिगमेंट और कॉपी ऑपरेटर्स ने इस पर रोक लगा दी
दानी

7
यह वास्तव में सबसे सीधा दृष्टिकोण है, भले ही यह वास्तव में ऐसा नहीं है जो सवाल पूछ रहा था। make_sared में कुछ अच्छी विशेषताएं हैं और मैं इसे जहां भी संभव हो उपयोग करने की कोशिश करता हूं, लेकिन इस स्थिति में यह काफी संभावना है कि make_sared के रन-टाइम प्रदर्शन लाभ अतिरिक्त कोड जटिलता और समारोह का उपयोग करने के लिए वास्तव में इसे उपयोग करने की आवश्यकता नहीं है। यदि आपको वास्तव में मेक_शेयर के प्रदर्शन की आवश्यकता है, तो पागल हो जाएं, लेकिन शेयर्ड_एप्ट्र के कंस्ट्रक्टर का उपयोग करने की सरलता को अनदेखा न करें।
केविन

स्मृति लीक के बारे में सावधान रहें ... इस सवाल को देखें 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_;
};

यह काफी हद तक ल्यूक डैंटन के जवाब के समान है, हालांकि इसे स्थानीय वर्ग में बदलना एक अच्छा स्पर्श है। कोड के साथ कुछ स्पष्टीकरण यह बेहतर जवाब दे सकता है।

आम तौर पर, मैं ऐसे छोटे फ़ंक्शन को हेडर फ़ाइल में लिखना चाहता हूं, लेकिन सीसी फ़ाइल नहीं। दूसरे, व्यवहार में, मैं एक मैक्रो का उपयोग करता हूं जो #define SharedPtrCreate (T) टेम्पलेट <टाइपनेम ... Arg> .....
अल्फा

अच्छा उत्तर। मैं भी एक मैक्रो को IMPLEMENT_CREATE_SHARED (ClassName)
ivan.ukr

8

चूंकि मुझे पहले से उपलब्ध कराए गए उत्तर पसंद नहीं थे, इसलिए मैंने खोज करने का फैसला किया और एक ऐसा समाधान ढूंढ लिया जो पिछले उत्तरों की तरह सामान्य नहीं है लेकिन मुझे यह बेहतर (टीएम) पसंद है। रेट्रोस्पेक्ट में यह ओम्निफ़ेरियस द्वारा प्रदान किए गए व्यक्ति की तुलना में बहुत अच्छा नहीं है, लेकिन ऐसे अन्य लोग भी हो सकते हैं जो इसे पसंद करते हैं :)

यह मेरे द्वारा आविष्कार नहीं किया गया है, लेकिन यह जोनाथन वैक्ली (जीसीसी डेवलपर) का विचार है।

दुर्भाग्य से यह सभी संकलक के साथ काम नहीं करता है क्योंकि यह std में एक छोटे से बदलाव पर निर्भर करता है :: कार्यान्वयन को आवंटित करें। लेकिन यह परिवर्तन अब मानक पुस्तकालयों के लिए एक प्रस्तावित अद्यतन है, इसलिए भविष्य में सभी संकलकों द्वारा इसे समर्थन मिल सकता है। यह जीसीसी 4.7 पर काम करता है।

C ++ मानक लाइब्रेरी वर्किंग ग्रुप चेंज रिक्वेस्ट यहाँ है: http://lwg.github.com/issues/lwg-active.html##12

उदाहरण के उपयोग के साथ GCC पैच यहां है: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html

समाधान एक कस्टम एलोकेटर है जिसे निजी कंस्ट्रक्टर के साथ कक्षा के लिए मित्र घोषित किया गया है, के साथ std :: आवंटित (बजाय std :: make_sared) का उपयोग करने के विचार पर काम करता है।

ओपी का उदाहरण इस तरह दिखेगा:

#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;
}

एक अधिक जटिल उदाहरण जो मैं काम कर रहा हूं उस उपयोगिता पर आधारित है। इससे मैं ल्यूक के समाधान का उपयोग नहीं कर सका। लेकिन Omnifarius द्वारा एक अनुकूलित किया जा सकता है। ऐसा नहीं है कि पिछले उदाहरण में, हर कोई MyAlloc का उपयोग करके एक वस्तु बना सकता है, जिसमें A (B) बनाने के अलावा कोई तरीका नहीं है () विधि।

#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

आदर्श रूप से, मुझे लगता है कि सही समाधान के लिए C ++ मानक में परिवर्धन की आवश्यकता होगी। एंड्रयू शेपलर निम्नलिखित का प्रस्ताव:

( पूरे सूत्र के लिए यहां जाएं )

हम बढ़ावा देने से एक विचार उधार ले सकते हैं :: iterator_core_access। मैं std::shared_ptr_accessबिना किसी सार्वजनिक या संरक्षित सदस्यों के साथ एक नया वर्ग प्रस्तावित करता हूं , और यह निर्दिष्ट करने के लिए कि std :: make_sared (args ...) और std :: आवंटन_समेद (a, args ...), भाव :: नया (pv) T (आगे (args) ...) और ptr-> ~ T () std के संदर्भ में अच्छी तरह से गठित होना चाहिए :: साझा_ptr_access।

Std का कार्यान्वयन :: share_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 सेंट को लिंक किए गए आइसोसप Google समूह में जोड़ने के लिए स्वतंत्र महसूस करें।


1
मुझे लगता है कि यह मानक के लिए एक अच्छा जोड़ है, लेकिन मेरे लिए इतना महत्वपूर्ण नहीं है कि मैं Google समूह में शामिल होने और टिप्पणी करने के लिए समय निकालूं और फिर उस समूह और टिप्पणी पर ध्यान दूं। :-)
सर्वव्यापी

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 करने के लिए लुभा रहा हूं। अन्य उत्तर (विशेष रूप से 'कुंजी वर्ग' उत्तर) काफी सुरुचिपूर्ण हैं और गैर-पोर्टेबल उत्तर बहुत ही बदसूरत हैं। मैं ऐसे कारण के बारे में नहीं सोच सकता जो आप गैर-पोर्टेबल उत्तर का उपयोग करेंगे। यह तेज या ऐसा कुछ नहीं है।
सर्वव्यापी

@Omnifarious यह वास्तव में गैर-पोर्टेबल है और मैं इसकी सिफारिश नहीं करूंगा, लेकिन मेरा मानना ​​है कि यह वास्तव में सबसे अधिक सही समाधान है। में मेरा उत्तर है, मैं जोड़ने के एक प्रस्ताव से लिंक std::shared_ptr_accessमानक है, जो एक सरल और पोर्टेबल तरह से ऊपर करने की अनुमति के रूप में देखा जा सकता है।
बोरिस डालस्टीन

3

एक और अधिक बालों वाली और दिलचस्प समस्या है जो तब होती है जब आपके पास दो कड़ाई से संबंधित ए और बी होते हैं जो एक साथ काम करते हैं।

कहते हैं कि A "मास्टर वर्ग" है और B इसका "दास" है। यदि आप केवल B के A को तत्काल प्रतिबंधित करना चाहते हैं, तो आप B के निर्माता को निजी बना देंगे, और मित्र B को A को इस तरह से बनाएंगे

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<>और इसके उपयोग का एक उदाहरण के लिए एक प्रतिक्रिया थी । मैंने इसे नीचे दिए गए फ़ैक्टरी टेम्प्लेट में अनुकूलित किया है, और इसे gcc C ++ 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";
}

[मूल] मुझे साझा सूचक एलियासिंग कंस्ट्रक्टर का उपयोग करके एक समाधान मिला। यह ctor और dtor दोनों को निजी बनाने की अनुमति देता है, साथ ही अंतिम स्पेसियर का उपयोग भी करता है।

#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_sared फ़ंक्शन नहीं है जो वास्तव में आपके निर्माता को कॉल कर रहा है इसलिए इसे मित्र बनाने से कोई फर्क नहीं पड़ता है।

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 :: _ Ref_count_obj वास्तव में आपके निर्माता को बुला रहा है, इसलिए इसे एक दोस्त बनने की आवश्यकता है। चूंकि यह थोड़ा अस्पष्ट है, इसलिए मैं एक मैक्रो का उपयोग करता हूं

#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;

तब आपकी कक्षा की घोषणा काफी सरल लगती है। यदि आप चाहें तो ptr और वर्ग को घोषित करने के लिए आप एकल मैक्रो बना सकते हैं।

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;

छुपाता है कि आप अपने स्मार्ट पॉइंटर को कैसे संभाल रहे हैं, आपको अपने टाइफाइड का उपयोग करना सुनिश्चित करना होगा। लेकिन अगर आपको हमेशा make_sared का उपयोग करके एक बनाना है, तो यह उद्देश्य को हरा देता है।

उपरोक्त उदाहरण आपके स्मार्ट पॉइंटर कंस्ट्रक्टर का उपयोग करने के लिए आपकी कक्षा का उपयोग करने के लिए कोड को बाध्य करता है, जिसका अर्थ है कि यदि आप स्मार्ट पॉइंटर के नए स्वाद पर स्विच करते हैं, तो आप अपनी कक्षा की घोषणा को बदलते हैं और आपके समाप्त होने का एक अच्छा मौका है। यह मत मानिए कि आपका अगला बॉस या प्रोजेक्ट किसी दिन इसे बदलने के लिए stl, बूस्ट आदि की योजना का उपयोग करेगा।

लगभग 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
सर्वव्यापी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.