typedef 共享指针的最佳策略是什么?
我有一个关于在冗长模板中使用 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
使用一个 WidgetPtr
(boost::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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
首先,这完全没有错 - 毕竟,客户端将(并且能够)使用接口的方式是接口本身的一部分;对于 C++ 来说,对象的内存管理不被垃圾收集,是其接口中相当重要的部分。
所以有两种情况。在一种情况下,
Widget
预计它将通过共享指针使用。这意味着例如。从小部件获取的子小部件作为shared_ptr
返回,创建的每个小部件都有它shared_ptr
等等。在与Widget
相同的标头中键入 defWidgetPtr
是完全合法的。在第二种情况下,
Widget
期望被管理,例如。通过普通的new
和delete
。客户端可以在特殊情况下使用shared_ptr
,但没有说明,例如。打印机对话例程不能使用auto_ptr
代替。客户端必须做好准备,如果wptr
是shared_ptr
,该行会导致灾难。
你的问题似乎表明后者是你的情况。所以恕我直言,你所做的一切都可以。客户端可以选择自己管理
Widget
对象的方式,只要不影响其他客户端即可。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 asshared_ptr
s, everywidget created has itshared_ptr
and so on. It would be totally legitimate to typedefWidgetPtr
in the same header asWidget
.In the second case,
Widget
s would expect to be managed eg. by ordinarynew
anddelete
. The clients can useshared_ptr
s in special cases, but nothing says eg. a printer dialogue routine can't useauto_ptr
instead. The clients have to be prepared that ifwptr
is ashared_ptr
, the lineleads 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.我不喜欢库规定特定智能指针的使用,但如果有必要,我会容忍它。
如果您希望强制用户始终使用
shared_ptr
来操作小部件,这是不可能的,所以不必费心去尝试。另一方面,如果
Widget
中有一个方法返回boost::shared_ptr
,那么提供(合理的)typedef 可能会简化客户端代码。因此,我会提倡使用内部 typedef:
在这种情况下,
#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 aboost::shared_ptr<Widget>
, then providing a (sane) typedef might simplify the client code.I would therefore promote the use of an inner typedef:
in which case it's perfectly okay to
#include
the necessary headers.在我看来你想太多了。无论如何,每个想要拥有
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 thetypedef
(which is a good idea imo) inWidget.h
makes 100% sense to me.我的方法(使用下划线类型,只是因为这就是我的做法)
我发现 const_ptr 版本非常有用。
My approach (using underbar types, just because it is how I do it)
I have found the const_ptr version is pretty darn useful.
我曾经将我的 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 calledFwd.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'ssrc
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 calledLibraryName.h
that includes theFwd.h
and all other public headers.Good luck!
第二部分首先:使用名称空间,即:
如果您想将其拆分:
您是库作者,您拥有名称空间,因此其他人不应入侵它。
现在第一部分也得到了解答,如果您选择可以这样做:
通过如上所述拆分命名空间。其效果是没有人必须使用这些实用程序,无论他们是否使用,他们都不应该侵入您的命名空间,因此他们可以自由地使 WidgetPtr 具有其他含义,只要它不在您的命名空间中即可。
Second part first: use a namespace, i.e.:
If you want to split it up:
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:
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.
我通常使用这种方法来简化输入并为类创建通用共享指针接口。
请注意它是 C++0x。
I generally use this approach to ease typing and makes a generic shared pointer interface for classes.
Do note it's C++0x.