Корень проблемы в том, что если функция или класс вашего друга делает вызовы вашего конструктора более низкого уровня, они также должны быть дружественными.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 лет, я заплатил большую цену за время, боль и побочные эффекты, чтобы починитьэто когда это было сделано неправильно много лет назад.