我有一个关于将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,这会产生不一致(因此,很可能是错误) - 整点如果给定Widget
,Widget
typedef应该作为一种类型。示例:我们不希望Widget
使用WidgetPtr
,Foo
的typedef,而WidgetPtr
使用WidgetPtr作为boost::shared_ptr
的typedef
另一种方法(以及我在网上讨论中提到的少数几种方法之一)是使typedef成为Bar
的公共成员,然后使用std::auto_ptr
:
Widget
同样,我不喜欢这个,因为(i)它表明指针类型在某种程度上是类的成员,并且(ii)它导致一个不稳定的接口。更糟糕的是:因为我编写的每个类都可能指向使用智能指针,我最终会追逐想象中的客户端尾部。丑陋,丑陋,丑陋。
就目前而言,我已从此代码库中删除了typedef(因为它们导致了严重的混淆,重复)并在所选函数中本地重新引入它们。这里再次出现了使用不一致的问题,但并不是那么严重。
我能想到的唯一其他解决方案 - 我不确定这是否被认为是一种好的做法 - 将是一个实用程序头,其中放置了typedef,可能在他们自己的命名空间内。在这个标题中,我们将包括并完成它。
我是否遗漏了一些明显的东西,或者这简直太棘手了?
PS-道歉的长度;我找不到一种更简单的方法来充分表达问题。
答案 0 :(得分:7)
此外,在Widget本身声明期间输入boost :: shared_ptr似乎没有意义 - 我们似乎将Widget的声明混合在一起,期待客户端如何使用Widget界面。
首先,这一点都没有错 - 毕竟,客户端将如何(并且可以)使用接口的方式是接口本身的一部分;对于C ++而言,对象的内存管理不是垃圾收集,而是其界面中相当重要的部分。
所以有两种情况。在一种情况下,Widget
会预期它将通过共享指针使用。这意味着,例如。从窗口小部件获取的子窗口小部件返回为shared_ptr
,创建的每个窗口小部件都有shared_ptr
,依此类推。在与WidgetPtr
相同的标题中输入Widget
是完全合法的。
在第二种情况下,Widget
s期望被管理,例如。通过普通new
和delete
。客户可以在特殊情况下使用shared_ptr
,但没有说明例如。打印机对话例程不能使用auto_ptr
。客户必须做好准备,如果wptr
是shared_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意味着别的东西,只要它不在你的命名空间中。