我有这样的代码,但我认为意图是明确的:

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指向的。有什么办法可以做到吗?


当前回答

查看20.7.2.2.6 shared_ptr创建[util.smartptr.shared]中std::make_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>();
}

其他回答

问题的根源在于,如果你加为好友的函数或类对你的构造函数进行低级调用,那么它们也必须加为好友。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年,我付出了巨大的时间代价、痛苦和副作用来修复多年前做错的事情。

如果可能的话,你可以创建一个公共移动构造函数,如下所示:

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

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

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

这个答案可能更好,也是我可能会接受的答案。但我也提出了一个更丑的方法,但仍然让一切仍然是内联的,不需要一个派生类:

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

Edit 2017-01-06:我对此进行了更改,以清楚地表明,这个思想可以清楚地、简单地扩展到接受参数的构造函数,因为其他人正在按照这些思路提供答案,并且似乎对此感到困惑。

这个解决方案怎么样,它很简单,也可以达到目标。 下面是代码片段:

#include <iostream>
#include <memory>
 
class Foo : public std::enable_shared_from_this<Foo> {
private:     //the user should not construct an instance through the constructor below.                    
    Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
    Foo& operator=(const Foo&) = delete;
    Foo& operator=(Foo&&) = default;

public:
    ~Foo() { std::cout << "Foo::~Foo\n"; } 

    int DoSth(){std::cout << "hello world" << std::endl; return 0;}

    std::shared_ptr<Foo> getPtr() { return shared_from_this();}

    static std::shared_ptr<Foo> Create() {
        Foo* foo = new Foo(5);
        return std::shared_ptr<Foo>(foo);
    }

private:
    int num_;

};

int main()
{
    auto sp = Foo::Create();
    sp->DoSth();

    Foo& foo = *sp.get();
    auto sp1 = foo.getPtr();

    std::cout << sp.use_count() << std::endl;
}

这可能是最简单的解决办法。基于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() {}  
};