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

发布于 2024-10-07 12:09:26 字数 1952 浏览 4 评论 0原文

我有一个关于在冗长模板中使用 typedef 的快速问题。关键是:我发现自己陷入了困境——除了客户端函数本地之外,似乎没有一个放置 typedef 的好地方。虽然也有类似的问题(请参阅此处 例如),似乎没有人能准确解决这个问题。请注意,这个问题并没有解决接下来的 typedef 是否合适的问题——出于说明的目的,我试图简化事情。

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

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

将此 typedef 放在 Widget 声明标头中看起来很难看。这里似乎有两个考虑因素:(i) 如果 Widget 本身在其成员中不使用共享指针,我们添加了一个额外的包含(因为我们无法转发声明 < code>boost::shared_ptr 模板类 - 如果我错了,请纠正我?) (ii) 如果我们想在另一个类的声明期间使用此 typedef(调用该类 Foo)我们通过包含 Widget.h 而不是简单地转发声明 Widget 或包含 WidgetFwd.h 来违反最佳实践...除非此 typedef在后者中重复。此外,在 Widget 本身的声明期间 typedef boost::shared_ptr 似乎没有意义 - 我们似乎正在混合 Widget 的声明,并预期客户端将如何使用 Widget 接口。

好吧,这很糟糕,但这更糟糕:如果我不尝试上述的某种组合,我最终会在客户端代码中得到重复的 typedef,这会产生不一致(因此可能会出现错误)——重点是给出的Widget,一个 WidgetPtr typedef 本身应该充当一种类型。示例:我们不希望 Foo 使用一个 WidgetPtrboost::shared_ptr 的 typedef),而 Bar< /code> 使用 WidgetPtr 作为 std::auto_ptr 的类型定义。

另一种方法(也是我在在线讨论中提到的少数方法之一)是使 typedef 成为 Widget 的公共成员,然后使用 Widget::Ptr

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

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

就目前情况而言,我已从该代码库中删除了 typedef(因为它们导致了严重的混乱和重复),并在选定的函数中在本地重新引入了它们。这里再次存在使用不一致的问题,但没有那么严重。

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

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

PS——对以上内容的长度表示歉意;我找不到更简单的方法来充分表达问题。

I have a quick question regarding the use of typedefs for lengthy templates. The crux: I've found myself in something of a pickle—there doesn't seem to be a good place to place typedefs except local to client functions. While there are similar SO questions (see here for example), none seem to address this exactly. Please note that this question doesn't address whether typedefs are desirable in what follows—I've tried to simplify things for expository purposes.

My problem has arisen while working with boost::shared_ptr<T>. Basically, I want to do the following:

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

Placing this typedef in the Widget declaration header seems ugly. There seem to be two considerations here: (i) if Widget itself doesn't make use of shared pointers in its members, we've added an additional include (as we can't forward declare the boost::shared_ptr template class—correct me if I'm wrong?) (ii) if we want to make use of this typedef during the declaration of another class (call that class Foo) we violate best practices by including Widget.h instead of simply forward declaring Widget or including WidgetFwd.h... unless this typedef is duplicated in the latter. Furthermore, it doesn't seem make sense to typedef boost::shared_ptr<Widget> during the declaration of Widget itself—we seem to be mixing Widget's declaration with an anticipation of how clients will make use of the Widget interface.

Okay, so that's bad, but this is worse: if I don't attempt some combination of the above I end up with duplicate typedefs in client code, which yields inconsistency (and hence, likely, error)—the whole point being that given Widget, a WidgetPtr typedef should act as a type in its own right. Example: we don't want Foo to make use of one WidgetPtr, a typedef of boost::shared_ptr, while Bar is using WidgetPtr as a typedef for std::auto_ptr.

Another method (and one of the few that I've seen mentioned in online discussion) would be to make the typedef a public member of Widget and then use Widget::Ptr:

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

Again, I don't like this as (i) it suggests that the pointer type is somehow a member of the class and (ii) it leads to a wonky interface. Worse still: since every class that I write can potentially be pointed to using smart pointers, I end up chasing the imaginary client's tail. Ugly, ugly, ugly.

As it stands, I've removed the typedefs from this codebase (as they led to serious confusion, duplication) and re-introduced them locally in selected functions. Here again there's a problem with inconsistent usage but it's not quite as severe.

The only other solution I can think of—and again I'm not sure whether this is considered good practice—would be to have a utilities header in which the typedefs are placed, potentially within their own namespace. In this header we'd include and be done with it.

Am I missing something obvious or is this just plain tricky?

PS—Apologies for the length of the above; I couldn't find a simpler way of fully expressing the problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

涙—继续流 2024-10-14 12:09:26

此外,在 Widget 本身的声明期间 typedef boost::shared_ptr 似乎没有意义——我们似乎将 Widget 的声明与对客户端将如何使用 Widget 接口的预期混合在一起。

首先,这完全没有错 - 毕竟,客户端将(并且能够)使用接口的方式是接口本身的一部分;对于 C++ 来说,对象的内存管理不被垃圾收集,是其接口中相当重要的部分。

所以有两种情况。在一种情况下,Widget 预计它将通过共享指针使用。这意味着例如。从小部件获取的子小部件作为 shared_ptr 返回,创建的每个小部件都有它 shared_ptr 等等。在与 Widget 相同的标头中键入 def WidgetPtr 是完全合法的。

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

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

会导致灾难。

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

Furthermore, it doesn't seem make sense to typedef boost::shared_ptr during the declaration of Widget itself—we seem to be mixing Widget's declaration with an anticipation of how clients will make use of the Widget interface.

First of all, this is not at all wrong - after all, the means of how the clients will (and can) use the interface is part of the interface itself; and for C++, not being garbage-collected, memory management of objects is a rather crucial part of their interface.

So there are two cases. In one case, the Widget would anticipate it would be used through a shared pointer. This would mean that eg. child widgets obtained from a widget are returned as shared_ptrs, everywidget created has it shared_ptr and so on. It would be totally legitimate to typedef WidgetPtr in the same header as Widget.

In the second case, Widgets would expect to be managed eg. by ordinary new and delete. The clients can use shared_ptrs in special cases, but nothing says eg. a printer dialogue routine can't use auto_ptr instead. The clients have to be prepared that if wptr is a shared_ptr, the line

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

leads to a disaster.

Your question seems to indicate the latter is your case. So IMHO, what you've done is OK. The clients can choose their means of managing Widget objects, as long as it doesn't affect other clients.

策马西风 2024-10-14 12:09:26

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

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

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

因此,我会提倡使用内部 typedef:

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

  Ptr AccessFirstChild();
}; // class Widget

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

I don't like a library dictating the use of a particular smart pointer, but I tolerate it if it is necessary.

If you wish to force users to always use a shared_ptr to manipulate a widget, it's impossible, so don't even bother trying.

On the other hand, if you have a method from Widget which returns a boost::shared_ptr<Widget>, then providing a (sane) typedef might simplify the client code.

I would therefore promote the use of an inner typedef:

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

  Ptr AccessFirstChild();
}; // class Widget

in which case it's perfectly okay to #include the necessary headers.

转身以后 2024-10-14 12:09:26

在我看来你想太多了。无论如何,每个想要拥有 shared_ptr 的人都必须包含 Widget 头文件。将 typedef (在我看来这是一个好主意)放入 Widget.h 对我来说 100% 有意义。

You are overthinking this in my opinion. Everybody who wants to have a shared_ptr<Widget> is going to have to include the Widget header file anyway. Putting the typedef (which is a good idea imo) in Widget.h makes 100% sense to me.

北风几吹夏 2024-10-14 12:09:26

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

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

我发现 const_ptr 版本非常有用。

My approach (using underbar types, just because it is how I do it)

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

I have found the const_ptr version is pretty darn useful.

○愚か者の日 2024-10-14 12:09:26

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

此外,每个公共标头将包含 Fwd.h,以便包含该标头会自动为您提供所有前向声明和指针类型定义。这在实践中非常有效。

不过,并非所有类都需要放入 shared_ptr 中。我只会为我希望动态创建的类型创建指针 typedef,在这种情况下,我将提供一个工厂。这还有一个额外的好处,即您可以不必仅提供具有接口类型的客户端代码,并将具体实现隐藏在库的 src 目录中。不是具体询问您的建议,但这给出了我的方法的完整画面。最后一点,还可以提供一个名为 LibraryName.h 的便捷标头,其中包含 Fwd.h 和所有其他公共标头。

祝你好运!

I used to structure my C++ code into libraries. A library would have a bunch of headers for client consumption, all inside the include/LibraryName directory. Also, I would have one header called Fwd.h inside this directory with forward declarations of all classes along with their pointer typedefs.

In addition, each public header would include Fwd.h so that including the header would automatically give you all forward declarations and pointer typedefs. This worked really well in practice.

Not all classes are necessary to place in a shared_ptr though. I would only create pointer typedefs for types that I expected to be created dynamically, and in this case I would supply a factory. This has the added benefit that you may get away with supplying client code with interface types only, and hide concreted implementations in your library's src directory. Not specifically what you asked advice for, but this gives the complete picture of my method. As a final point, it's also possible to supply a convenience header called LibraryName.h that includes the Fwd.h and all other public headers.

Good luck!

谎言 2024-10-14 12:09:26

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

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

如果您想将其拆分:

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

您是库作者,您拥有名称空间,因此其他人不应入侵它。

现在第一部分也得到了解答,如果您选择可以这样做:

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

通过如上所述拆分命名空间。其效果是没有人必须使用这些实用程序,无论他们是否使用,他们都不应该侵入您的命名空间,因此他们可以自由地使 WidgetPtr 具有其他含义,只要它不在您的命名空间中即可。

Second part first: use a namespace, i.e.:

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

If you want to split it up:

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

You're the library author, you own the namespace, so no one else should invade it.

And now part one is answered too, if you choose you can do:

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

by splitting up the namespace as above. The effect is no one has to use the utilities, whether or not they do they should not invade your namespace, so they're free to make WidgetPtr mean something else, as long as it isn't in your namespace.

无声无音无过去 2024-10-14 12:09:26

我通常使用这种方法来简化输入并为类创建通用共享指针接口。
请注意它是 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);
}

I generally use this approach to ease typing and makes a generic shared pointer interface for classes.
Do note it's 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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文