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

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


当前回答

当两个严格相关的类a和类B一起工作时,会出现一个更麻烦也更有趣的问题。

假设A是“主类”,B是“从类”。如果你想将B的实例化限制为A,你可以将B的构造函数设为private,并将B设为A的友例

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

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

不幸的是,从a的方法调用std::make_shared<B>()将使编译器抱怨B::B()是私有的。

我对此的解决方案是在B内部创建一个公共Pass虚拟类(就像nullptr_t一样),它有私有构造函数,与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);
  }
};

其他回答

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

这个怎么样?

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

因为我不喜欢已经提供的答案,所以我决定继续搜索,并找到了一个解决方案,它不像之前的答案那么通用,但我更喜欢它(tm)。回想起来,它并不比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;
}

一个基于我正在使用的实用程序的更复杂的示例。在这种情况下,我不能使用卢克的解决方案。但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;
}

这里有一个简单的解决方案:

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