typedef'ing共享指针的最佳策略是什么?

时间:2010-12-13 13:48:05

标签: c++ boost typedef shared-ptr

我有一个关于将typedef用于冗长模板的快速问题。症结:我发现自己处于一种腌制状态 - 似乎没有一个将typedef放在除客户端函数本地之外的好地方。虽然存在类似的 SO 问题(例如,请参阅here),但似乎没有人提出这些问题。请注意,这个问题并没有解决下面的typedef是否可取的问题 - 我试图简化出于说明目的。

使用boost::shared_ptr<T>时出现了问题。基本上,我想做以下事情:

#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<Widget> WidgetPtr;

将此typedef放在Widget声明标题中似乎很难看。这里似乎有两个注意事项:(i)如果Widget本身没有在其成员中使用共享指针,我们添加了一个额外的包含(因为我们无法转发声明{{1}如果我错了,模板类 - 纠正我吗?)(ii)如果我们想在声明另一个类(调用该类boost::shared_ptr)期间使用这个typedef,我们通过包含{{1}来违反最佳实践而不是简单地向前声明Foo或包括Widget.h ...除非在后者中重复此typedef。此外,在Widget本身的声明中输入WidgetFwd.h似乎没有意义 - 我们似乎在混合boost::shared_ptr<Widget>的声明,期待客户如何使用Widget界面。

好吧,所以这很糟糕,但这更糟糕:如果我没有尝试上述的某种组合,我最终会在客户端代码中出现重复的typedef,这会产生不一致(因此,很可能是错误) - 整点如果给定WidgetWidget typedef应该作为一种类型。示例:我们不希望Widget使用WidgetPtrFoo的typedef,而WidgetPtr使用WidgetPtr作为boost::shared_ptr的typedef

另一种方法(以及我在网上讨论中提到的少数几种方法之一)是使typedef成为Bar的公共成员,然后使用std::auto_ptr

Widget

同样,我不喜欢这个,因为(i)它表明指针类型在某种程度上是类的成员,并且(ii)它导致一个不稳定的接口。更糟糕的是:因为我编写的每个类都可能指向使用智能指针,我最终会追逐想象中的客户端尾部。丑陋,丑陋,丑陋。

就目前而言,我已从此代码库中删除了typedef(因为它们导致了严重的混淆,重复)并在所选函数中本地重新引入它们。这里再次出现了使用不一致的问题,但并不是那么严重。

我能想到的唯一其他解决方案 - 我不确定这是否被认为是一种好的做法 - 将是一个实用程序头,其中放置了typedef,可能在他们自己的命名空间内。在这个标题中,我们将包括并完成它。

我是否遗漏了一些明显的东西,或者这简直太棘手了?

PS-道歉的长度;我找不到一种更简单的方法来充分表达问题。

7 个答案:

答案 0 :(得分:7)

  

此外,在Widget本身声明期间输入boost :: shared_ptr似乎没有意义 - 我们似乎将Widget的声明混合在一起,期待客户端如何使用Widget界面。

首先,这一点都没有错 - 毕竟,客户端将如何(并且可以)使用接口的方式是接口本身的一部分;对于C ++而言,对象的内存管理不是垃圾收集,而是其界面中相当重要的部分。

所以有两种情况。在一种情况下,Widget会预期它将通过共享指针使用。这意味着,例如。从窗口小部件获取的子窗口小部件返回为shared_ptr,创建的每个窗口小部件都有shared_ptr,依此类推。在与WidgetPtr相同的标题中输入Widget是完全合法的。

在第二种情况下,Widget s期望被管理,例如。通过普通newdelete。客户可以在特殊情况下使用shared_ptr,但没有说明例如。打印机对话例程不能使用auto_ptr。客户必须做好准备,如果wptrshared_ptr,那么

shared_ptr<Widget> w2(wptr->firstChild()->parent());

导致灾难。

你的问题似乎表明后者就是你的情况。恕我直言,你所做的就是好的。客户可以选择管理Widget对象的方式,只要它不影响其他客户端。

答案 1 :(得分:6)

我不喜欢指定使用特定智能指针的库,但如果有必要,我会容忍它。

如果您希望强制用户始终使用shared_ptr来操作窗口小部件,那么这是不可能的,所以不要费心去尝试。

另一方面,如果你有Widget的方法返回boost::shared_ptr<Widget>,那么提供(合理的)typedef可能会简化客户端代码。

因此我推广使用内部typedef:

class Widget
{
public:
  typedef boost::shared_ptr<Widget> Ptr;

  Ptr AccessFirstChild();
}; // class Widget

在这种情况下,#include必要的标题是完全可以的。

答案 2 :(得分:5)

在我看来,你是在思考这个问题。想要拥有shared_ptr<Widget>的每个人都必须包含Widget头文件。将typedef(这是一个好主意imo)放在Widget.h中对我来说是100%有道理。

答案 3 :(得分:3)

我的方法(使用下划线类型,只是因为我是这样做的)

class Type
{
    public:
        typedef shared_ptr<Type>        ptr;
        typedef shared_ptr<const Type>  const_ptr;
};

我发现const_ptr版本非常有用。

答案 4 :(得分:1)

我曾经将我的C ++代码构建到库中。一个库将有一堆标题供客户端使用,所有这些都在include/LibraryName目录中。此外,我将在此目录中有一个名为Fwd.h的标头,其中包含所有类的前向声明及其指针typedef。

此外,每个公共标头都包含Fwd.h,因此包含标头会自动为您提供所有前向声明和指针typedef。这在实践中非常有效。

并非所有课程都必须放在shared_ptr中。我只会为我希望动态创建的类型创建指针typedef,在这种情况下我会提供一个工厂。这样做的另一个好处是,您可以仅使用接口类型提供客户端代码,并隐藏库src目录中的混合实现。没有具体你提出的建议,但这给出了我的方法的完整图片。最后,还可以提供名为LibraryName.h的便捷标题,其中包含Fwd.h和所有其他公共标题。

祝你好运!

答案 5 :(得分:1)

我通常使用这种方法来简化输入,并为类创建通用的共享指针接口。 请注意它是C ++ 0x。

#include <iostream>
#include <memory>

template <class T>
struct SharedVirtual
{
   typedef std::shared_ptr<T> VPtr;
};

template <class T>
struct Shared
{
   typedef std::shared_ptr<T> Ptr;

   template <class... P>
   static Ptr ptr(P&&... args) 
   { 
      return std::make_shared<T>(std::forward<P>(args)...); 
   }
};

class Foo : public SharedVirtual<Foo>
{
   public:
      virtual void foo() const = 0;
};

class Test : public Foo, public Shared<Test>
{
   public:
      void foo() const { std::cout << "Hai u!" << std::endl; }
};

void print(const Foo::VPtr& ptr)
{
   ptr->foo();
}

int main()
{
   auto ptr = Test::ptr();
   print(ptr);
}

答案 6 :(得分:0)

第二部分第一部分:使用名称空间,即:

namespace WidgetStuff {
  class Widget { ..
  typedef shared_ptr<Widget> WidgetPtr;
  ..

如果你想拆分它:

namespace WidgetStuff {
   class Widget { ...
}
...
namespace WidgetStuff { 
  typedef ...

您是图书馆作者,拥有命名空间,因此没有其他人可以入侵它。

现在第一部也回答了,如果你选择的话可以做到:

#include <widget.h>
#include <widget_utils.h>

通过拆分命名空间如上所述。效果是没有人必须使用实用程序,无论他们是否不应该侵入你的命名空间,所以他们可以自由地使WidgetPtr意味着别的东西,只要它不在你的命名空间中。