通过基类的“create”方法仅创建共享指针对象

3
我想实现应用程序中的某些对象只能通过调用名为“create”的静态方法来构造为shared_ptr。
当然,我可以直接在所有相关类中添加静态“create”方法。但是,这意味着我必须在几乎所有类中重复非常相似的代码。宏可能有效,但我认为这不太优雅。
我提出了一种替代方法,即从一个模板化的“BaseObject”类派生所有类,该类实现“create”方法并返回指针。这几乎可行,但是当其为受保护时,std::make_shared无法访问其子类的构造函数。
非解决方案是将子类构造函数设置为public(请参见下面的(1))。但是现在Foo可以正常构造,这将使整个点失去意义。另一种解决方案是将BaseObject设置为子类的友元,并直接使用shared_ptr(请参见下面的(2))。
这两种解决方案都给子类的实现者增加了额外的负担。因为他们必须找到另一种使构造函数非公开的方法或放置friend声明。第二种解决方案还有一个问题,就是不能使用更高效的make_shared。
我的问题是:是否有更好的方法来做到这一点?
template<class T>
class BaseObject
{
public:
    typedef std::shared_ptr<T> SharedPtr;

    template<class... Args>
    static typename BaseObject<T>::SharedPtr create(Args&&... args)
    {
        return std::make_shared<T>(args...);
        //return std::shared_ptr<T>(new T(args...)); (2)
    }
};

class Foo : public BaseObject<Foo>
{
//friend BaseObject<Foo>; (2)

protected: //public (1)
    Foo(int a = 0) : m_foo(a) {}

private:
    int     m_foo;
};


int main(int argc, char* argv[])
{
    Foo::SharedPtr bla = Foo::create(1);
    return 0;
}

更新:

在此刻,他们的传递密钥习语似乎为我提供了最佳解决方案:

template<class T>
class BaseObject
{
public:
    typedef std::shared_ptr<T> SharedPtr;

    class Key
    {
        friend class BaseObject<T>;
        Key() {}
    };

    template<class... Args>
    static typename BaseObject<T>::SharedPtr create(Args&&... args)
    {
        return std::make_shared<T>(Key(), args...);
    }
};

class Foo : public BaseObject<Foo>
{
public:
    Foo(BaseObject<Foo>::Key, int a = 0) : m_foo(a) {}

private:
    int     m_foo;
};

好处:

  • 只有通过Foo::create作为shared_ptr创建Foo对象才有可能。
  • 无需在Foo中添加复杂的友元声明。
  • std::make_shared仍然有效。

这种解决方案唯一的问题是要求将“Key”作为构造函数的第一个参数。但我可以接受这个问题。

1个回答

2

“更好”是主观的,但我认为如果你将构造函数设为私有,并使std::make_shared成为友元函数,那么这样会更加直观。这样唯一能够创建对象的函数就是std::make_shared,你可以这样写:

std::shared_ptr<Foo> ptr = std::make_shared<Foo>(12);

替代方案:

Foo::SharedPtr bla = Foo::create(1);

因此,任何未来阅读您的代码的人都可以理解您在Foo类中的意思,而无需实际查看它。

更新

我已经尝试了我写的内容,但并没有真正起作用。这里有一个类似问题的答案,很可能也适用于您的问题:

使用受保护的构造函数和抽象接口进行make_shared

更新2

以下是如何使其工作(VC++2013)

#include <memory>

using namespace std;

class Foo
{
protected:
    Foo(int a = 0) : m_foo(a) {}

private:
    int     m_foo;

    friend  shared_ptr<Foo> make_shared<>();
    friend class _Ref_count_obj<Foo>;
};



int main()
{
  shared_ptr<Foo> foo = make_shared<Foo, int>(12);
  return 0;
}

_Ref_count_obj 是由 make_shared 内部使用的,因此您也需要将其设置为友元。


另外,您应该从enable_shared_from_this中派生。详见此处:https://dev59.com/-3RB5IYBdhLWcg3wH0SW - Sarang
@Sarang 是的,如果他使用boost,但在他的例子中,它明确是一个STL共享指针。 - Gábor Angyal
@GáborAngyal 感谢您的回答。这种解决方案的主要问题在于,将 std::make_shared 设为 friend 会非常依赖具体实现。VC++ 标准库的更新可能会破坏所有代码,并且在 libc++ 和 libstdc++ 上需要完全不同的代码。从用户和维护的角度来看,这对我来说是行不通的。 - wmkn
但是如果你将make_shared声明为友元,那么你就允许任何地方创建对象实例作为共享指针。 - Muhammet Ali Asan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接