如何在仅具有受保护或私有构造函数的类上调用:: 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并防止任何人制作此类的对象,而这些对象不是由a指向的::std::shared_ptr。有什么办法可以做到这一点?


您可以将需要构造函数的功能标记为深层功能,但不能移植。
丹妮

@Dani:是的,拥有一个便携式解决方案会很好。但这会起作用。
2011年

Answers:


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年1月6日:我进行了更改,以明确地表明此思想对于带有参数的构造函数很容易扩展,因为其他人正在按照这些思路提供答案,并且对此感到困惑。


14
实际上,我非常喜欢那些仅用作键的无意义的结构。我更喜欢Luc的解决方案,但这可能是我反对继承的偏见。
Matthieu M.

2
同意,我也更好。
ildjarn 2011年

3
@Berkus:然后protected代替private。通过“它”,我指的是this_is_private类,在这种情况下,应重新命名。我通常constructor_access在代码中称呼它。
dalle 2014年

1
可悲的是,如果您的构造函数采用实参,则此方法将无效。在这种情况下,您可以简单地传递{}私有标签,而无需访问类型名称(已通过g ++ 4.9.0测试)。没有真正的参数,它会尝试A从{} 进行构造,尽管我不知道为什么,但是失败了。我认为将this_is_private构造函数设为私有并提供静态方法来创建它可以解决该问题,因为除非您在成员函数签名中泄漏了类型,否则应该无法从外部访问此方法。
Stefan 2014年

3
斯特凡(Stefan),如果您给this_is_private一个私人老师,可以让A类成为朋友。似乎可以弥补漏洞。
史蒂文·克莱默

78

查看std::make_shared20.7.2.2.6 shared_ptr创建[util.smartptr.shared.create]第1段中的要求:

要求:表达::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
哦,这是一个非常聪明的答案,可能比我想到的另一个答案要好。
2011年

但是,有一个问题,shared_ptr不会删除A而不删除concrete_A,这不会引起问题吗?
2011年

8
嗯,这是因为shared_ptr在实例化时存储了删除程序,如果您使用的make_shared是删除程序,则绝对必须使用正确的类型。
2011年

1
@LucDanton问题不是关于接口的问题,因为标题表明他还要求提供私人简历。另外,这就是为什么我仍然在这个问题上。一些带有machiavelli类的旧代码,这些类具有私有的ctor和返回原始指针的create方法,我正在尝试将它们转换为智能指针。
zahir 2015年

2
我喜欢这种方法(自己使用),但是您确实需要一个虚拟析构函数。它可以很好地扩展到带有参数的构造函数(只需提供一个直通构造函数)。而且,如果您使用的是受保护的而不是私有的,则可以使标题用户完全看不到它。
乔·斯蒂尔

69

可能是最简单的解决方案。基于Mohit Aron 先前的回答并结合了dlf的建议。

#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 2015年

2
@anton_rh:您不能向内部类添加模板参数。看这里
bobbel

3
嗯...看来你是对的。在我的情况下,结构是不是本地的,但是是一个私人的结构:class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }。看到这里cpp.sh/65qbr
anton_rh

这很好。是否有可能使它成为可继承的属性,因此不必重复多次此模式?尤其是公开非默认构造函数的版本对我来说非常有趣。默认版本将“仅”需要一些语法构造,以任何继承类的类替换A。我不知道这样的事情,但是得知它的存在我不会感到惊讶……
Kjeld Schmidt

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 2014年

很棒的主意Mohit对我有很大帮助。
Jnana

12

这个怎么样?

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

13
很好。但是::std::make_shared具有超越简单地对某些内容进行shared_ptr的功能。它将引用计数与对象一起分配,以便它们彼此靠近。我真的非常想使用::std::make_shared
2011年

删除的分配和复制运算符禁止这样做
Dani

7
这实际上是最直接的方法,即使它并不是真正要问的问题。make_shared确实具有一些不错的特性,我会尽可能使用它,但是在这种情况下,make_shared的运行时性能优势似乎并没有超过使用它实际需要的额外代码复杂性和仪式。如果您确实需要make_shared的性能,那就疯了,但不要忽略仅使用shared_ptr的构造函数的简单性。
凯文(Kevin)

不过请注意内存泄漏...请参阅此问题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_;
};

这与Luc Danton的答案大致相同,尽管将其转换为本地课程是一个不错的选择。伴随代码进行一些解释可以使答案更好。

通常,我想在头文件中而不是在cc文件中写这么小的函数。其次,在实践中,我使用宏,它看起来像的#define SharedPtrCreate(T)模板<类型名...精氨酸> .....
阿尔法

好答案。我什至将其放入名为IMPLEMENT_CREATE_SHARED(ClassName)之类的宏
ivan.ukr

8

由于我不喜欢已经提供的答案,因此我决定进行搜索,发现了一个不像以前的答案那样通用的解决方案,但我更喜欢它。回想起来,它并不比Omnifarius提供的更好,但是可能还有其他人也喜欢它:)

这不是我发明的,而是Jonathan Wakely(GCC开发人员)的想法。

不幸的是,由于它依赖于std :: allocate_shared实现中的微小变化,因此它不适用于所有编译器。但是,此更改现在是对标准库的建议更新,因此将来所有编译器都可能支持此更改。它适用于GCC 4.7。

C ++标准库工作组更改请求位于此处:http : //lwg.github.com/issues/lwg-active.html#2070

带有示例用法的GCC修补程序在这里:http : //old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html

该解决方案基于将std :: allocate_shared(而不是std :: make_shared)与自定义分配器结合使用的想法,该自定义分配器被声明为与私有构造函数成为类的朋友。

OP中的示例如下所示:

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

一个基于我正在使用的实用程序的更复杂的示例。因此,我无法使用Luc的解决方案。但是Omnifarius的那个可以改编。并不是说在上一个示例中每个人都可以使用MyAlloc创建一个A对象,但是除了create()方法之外没有其他方法可以创建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 ++标准进行补充。Andrew Schepler提出以下建议:

在这里查看整个主题)

我们可以借鉴boost :: iterator_core_access的想法。我提出了一个std::shared_ptr_access没有公共或受保护成员的新类,并为std :: make_shared(args ...)和std :: alloc_shared(a,args ...)指定了表达式:: new(pv)T (forward(args)...)和ptr->〜T()在std :: shared_ptr_access的上下文中必须格式正确。

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美分添加到链接的isocpp 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());
}

我已经在Windows和Linux上进行了测试,可能需要针对不同的平台进行调整。


1
由于缺乏可移植性,我倾向于将其设为-1。其他答案(尤其是“键类”答案)相当优雅,非便携式答案非常难看。我想不出您使用非便携式答案的原因。它不是更快或类似的东西。
2016年

@Omnifarious它确实是不可移植的,我不建议这样做,但是我认为这实际上是语义上最正确的解决方案。在我的回答中,我链接到std::shared_ptr_access对标准进行添加的提案,该提案可以被视为允许以简单且可移植的方式进行上述操作。
鲍里斯·达尔斯泰因

3

当您有两个紧密相关的类A和B一起工作时,会发生一个更加毛骨悚然和有趣的问题。

说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_tB,该类具有私有构造函数,并且与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";
}

最后,有人说clang抱怨Factory :: Type在用作朋友时是私有的,因此,在这种情况下,请将其公开。暴露无害。


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 :: _ 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;

稍微隐藏了如何处理智能指针,您必须确保使用typedef。但是,如果您始终必须使用make_shared创建一个,则无法达到目的。

上面的示例强制使用类的代码使用智能指针构造函数,这意味着,如果切换到新的智能指针风格,则会更改类声明,并且很有可能完成。不要以为下一个老板或项目有一天会使用stl,boost等进行更改的计划。

这样做将近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
Omnifarious
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.