这个长问题道歉,但一些上下文是必要的。 我有一些代码,对于我正在处理的项目来说似乎是一种有用的模式:
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。
将智能指针类型封装在类中具有以下几个优点:
- 它允许您控制指针类型的可复制性和可移动性。
- 它对调用方隐藏shared_ptr详细信息,以便可以将非平凡的对象构造(例如抛出异常的对象构造)放置在 Instance() 调用中。 在使用
- 使用多个智能指针实现的项目时,可以更改基础智能指针类型。 您可以切换到特定类的unique_ptr甚至原始指针,调用代码将保持不变。
- 它将有关(智能)指针构造和混叠的详细信息集中在最了解如何操作的类中。
- 它允许您决定哪些类可以使用智能指针,以及必须在堆栈上构造哪些类。 PointerType 字段的存在为调用方提供了关于可以创建哪些类型的与类对应的指针的提示。 如果没有为类定义 PointerType,则表示无法创建指向该类的指针;因此,必须在堆栈上创建该特定类,RAII 样式。
但是,我认为没有明显的方法可以将这段代码应用于我项目中的所有类,而无需直接键入必要的 typedef 和静态 PointerType Instance() 函数。 我怀疑应该有一些一致的、C++11 标准的跨平台方法来使用基于策略的模板来做到这一点,但一些实验并没有找到一种明显的方法,以一种在所有现代C++编译器上干净编译的方式将其简单地应用于一堆类。
你能想到一种优雅的方法将这些概念添加到一堆类中,而无需大量的剪切和粘贴吗? 理想的解决方案将在概念上限制可以为哪些类型的类创建哪些类型的指针(一个类使用shared_ptr,另一个类使用原始指针),并且它还将通过自己的首选方法处理任何受支持类型的实例化。 这样的解决方案甚至可以处理和/或限制强制,通过在编译时适当地失败,在非标准和标准智能和哑指针类型之间
一种方法是使用奇怪的重复出现的模板模式。
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
,他们可以创建一个。如果他们想在堆栈上创建一个对象,他们可以。我认为强制要求如何创建和管理用户的对象没有任何好处。
为了解决您的问题:
- 它允许您控制指针类型的可复制性和可移动性。
这有什么好处?
- 它对调用方隐藏shared_ptr详细信息,以便可以将非平凡的对象构造(例如抛出异常的对象构造)放置在 Instance() 调用中。
我不确定你在这里的意思。希望不是您可以捕获异常并返回nullptr
.那将是Java级的坏事。
在使用
- 使用多个智能指针实现的项目时,可以更改基础智能指针类型。您可以切换到特定类的unique_ptr甚至原始指针,调用代码将保持不变。
如果您正在使用多种智能指针,也许最好让用户为给定情况选择适当的类型。此外,我认为具有相同的调用代码但返回不同类型的句柄可能会令人困惑。
- 它将有关(智能)指针构造和混叠的详细信息集中在最了解如何操作的类中。
在什么意义上,一个类"最"知道如何进行指针构造和锯齿?
- 它允许您决定哪些类可以使用智能指针,以及必须在堆栈上构造哪些类。PointerType 字段的存在为调用方提供了关于可以创建哪些类型的与类对应的指针的提示。如果没有为类定义 PointerType,则表示无法创建指向该类的指针;因此,必须在堆栈上创建该特定类,RAII 样式。
同样,我从根本上不同意必须以某种方式创建和管理某种类型的对象的想法。这就是单例模式如此阴险的原因之一。
我不建议添加这些静态函数。 在其他缺点中,当有多个构造函数时,它们的创建和维护确实非常繁琐。 在这种情况下,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 );
我想你会发现这在打字时不会太麻烦。 当然,这都是风格问题。
对约瑟夫答案的改进,以使指针类型更易于配置:
#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
所示。