C ++ 11 make_shared实例化

时间:2016-03-12 04:30:57

标签: c++ templates c++11 shared-ptr smart-pointers

为长期问题道歉,但有些背景是必要的。我有一些代码似乎是我正在进行的项目的有用模式:

class Foo
{
public:
    Foo( int bar = 1 );
    ~Foo();
    typedef std::shared_ptr< Foo > pointer_type;
    static pointer_type make( int bar = 1 )
    {
        return std::make_shared< Foo >( bar );
    }

...
}

正如您所看到的,它提供了一种将任何类构造为PointerType的简单方法,该类将shared_ptr封装到该类型中:

auto oneFoo = Foo::make( 2 );

因此,您可以获得shared_ptr的优势,而无需在整个代码库中引用make_shared和shared_ptr。

将智能指针类型封装在类中有以下几个优点:

  1. 它允许您控制指针类型的可复制性和可移动性。
  2. 它隐藏了来自调用者的shared_ptr细节,因此可以在Instance()调用中放置非平凡的对象构造,例如抛出异常的构造。
  3. 当您使用多个智能指针实现的项目时,您可以更改基础智能指针类型。您可以切换到unique_ptr甚至是特定类的原始指针,并且调用代码将保持不变。
  4. 它集中了关于(智能)指针构造和别名的详细信息,这些关于最了解如何操作的类。
  5. 它允许您决定哪些类可以使用智能指针以及哪些类必须在堆栈上构建。 PointerType字段的存在向调用者提供了关于可以为类创建哪些类型的指针的提示。如果没有为类定义PointerType,这将表明不能创建指向该类的指针;因此,必须在堆栈上创建特定的类,RAII样式。
  6. 但是,我没有看到将这些代码应用于项目中的所有类的明显方法,而无需直接键入必需的typedef和静态PointerType Instance()函数。我怀疑应该有一些一致的,C ++ 11标准,跨平台的方式来实现这一点与基于策略的模板,但一些实验并没有找到一个明显的方式将这个简单地应用于一堆类中的一个在所有现代C ++编译器上干净地编译的方法。

    你能想到一种优雅的方式将这些概念添加到一堆类中,而不需要大量的剪切和粘贴吗?理想的解决方案是概念性地限制可以为哪些类型的类创建哪些类型的指针(一个类使用shared_ptr而另一个类使用原始指针),并且它还将通过其自己的首选方法处理任何受支持类型的实例化。这种解决方案甚至可能通过在编译时适当地在非标准和标准智能和哑指针类型之间进行处理和/或限制强制。

3 个答案:

答案 0 :(得分:10)

一种方法是使用curiously recurring template pattern

template<typename T>
struct shared_factory
{
    using pointer_type = std::shared_ptr<T>;

    template<typename... Args>
    static pointer_type make(Args&&... args)
    {
        return std::make_shared<T>(std::forward<Args>(args)...);
    }
};

struct foo : public shared_factory<foo>
{
    foo(char const*, int) {}
};

我相信这会给你你想要的东西。

foo::pointer_type f = foo::make("hello, world", 42);

然而...

我不建议使用这种方法。试图规定类型的用户如何实例化该类型是不必要的限制。如果他们需要std::shared_ptr,他们可以创建一个。如果他们需要std::unique_ptr,他们可以创建一个。如果他们想在堆栈上创建一个对象,他们可以。通过强制创建和管理用户对象的方式,我看不到任何好处。

解决您的观点:

  
      
  1. 它允许您控制指针类型的可复制性和可移动性。
  2.   

这有什么好处?

  
      
  1. 它隐藏了来自调用者的shared_ptr细节,因此可以在Instance()调用中放置非平凡的对象构造,例如抛出异常的构造。
  2.   

我不确定你的意思。希望不是你可以捕获异常并返回nullptr。那将是Java级别的糟糕。

  
      
  1. 当您使用多个智能指针实现的项目时,您可以更改基础智能指针类型。您可以切换到unique_ptr甚至是特定类的原始指针,并且调用代码将保持不变。
  2.   

如果您正在使用多种智能指针,或许最好让用户为给定情况选择合适的类型。此外,我认为拥有相同的调用代码但返回不同类型的句柄可能会造成混淆。

  
      
  1. 它集中了关于(智能)指针构造和别名的详细信息,这些关于最了解如何操作的类。
  2.   

一个类在什么意义上“最”知道如何进行指针构造和别名?

  
      
  1. 它允许您决定哪些类可以使用智能指针以及哪些类必须在堆栈上构建。 PointerType字段的存在向调用者提供了关于可以为类创建哪些类型的指针的提示。如果没有为类定义PointerType,这将表明不能创建指向该类的指针;因此,必须在堆栈上创建特定的类,RAII样式。
  2.   

同样,我基本上不同意某种类型的对象必须以某种方式创建和管理的想法。这是singleton pattern如此阴险的原因之一。

答案 1 :(得分:2)

我不建议添加那些静态函数。除了其他缺点之外,当有多个构造函数时,它们在创建和维护时会非常繁重。这是一个auto可以帮助以及类外的typedef的情况。另外,您可以使用std命名空间(但请不要在标题中):

class Foo
{
public:
    Foo();
    ~Foo();
    Foo( int bar = 1 );

...
}

typedef std::shared_ptr<Foo> FooPtr;

在C ++文件中:

using namespace std;
auto oneFoo = make_shared<Foo>( 2 );
FooPtr anotherFoo = make_shared<Foo>( 2 );

我想你会发现打字时不会太累赘。当然,这都是风格问题。

答案 2 :(得分:2)

为了使指针更具可配置性,这是对约瑟夫答案的改进:

#include <memory>

template <typename T, template <typename...> class PtrT = std::shared_ptr>
struct ptr_factory {
    using pointer_type = PtrT<T>;

    template <typename... Args>
    static pointer_type make(Args&&... args) {
        return pointer_type{new T{args...}};
    }
};

template <typename T>
struct ptr_factory<T, std::shared_ptr> {
    using pointer_type = std::shared_ptr<T>;

    template <typename... Args>
    static pointer_type make(Args&&... args) {
        return std::make_shared<T>(args...);
    }
};

struct foo : public ptr_factory<foo> {
        foo(char const*, int) {}
};

struct bar : public ptr_factory<bar, std::unique_ptr> {
    bar(char const*, int) {}
};

ptr_factory默认使用std::shared_ptr,但可以配置为使用不同的智能指针模板,这要归功于模板模板参数,如struct bar所示。