我在一个项目中广泛使用shared_ptr和STL,这导致类型过长、容易出错,例如shared_ptr< vector< shared_ptr > >(我偏爱ObjC编程,在那里长名称是常见的,但即便如此,这也太多了)。我认为,要更清晰,最好一致地称其为FooListPtr,并记录命名惯例,“Ptr”表示shared_ptr,“List”表示vector of shared_ptr。
首先,我建议使用良好的设计结构来进行作用域限定(例如命名空间),并为typedef选择具有描述性、非缩写的名称。我认为FooListPtr太短了。没有人想猜测缩写的含义(或者惊讶地发现Foo是const、shared等),也没有人想仅仅因为作用域冲突而修改自己的代码。
在您的库(以及其他常见类别)中选择typedef的前缀可能也有所帮助。
将类型拖出其声明的作用域也是一个坏主意:
namespace MON {
namespace Diddy {
class Foo;
}
typedef Diddy::Foo Diddy_Foo;
}
这有一些例外情况:
顺便说一下,在命名空间作用域和命名空间别名中使用“using”应该被避免 - 如果想要最小化未来维护,请限定作用域。
虽然这很容易typedef,但它会造成头文件的问题。我似乎有几个选项可以定义FooListPtr:
Foo.h。这会交错所有头文件,并创建严重的构建问题,因此不可行。
对于确实依赖于其他声明的声明可能是一个选项,暗示您需要划分软件包,或者子系统有一个公共的本地化接口。
FooFwd.h(“前向标头”)。Effective C++基于iosfwd.h建议使用此方法。这非常一致,但是维护两倍数量的标头的开销似乎最多令人讨厌。
真的不用担心这个维护问题。这是一个好习惯。编译器使用前向声明和typedefs代价微小。它并不烦人,因为它有助于减少依赖关系,并确保它们都是正确和可见的。其他文件引用“包类型”头文件,因此实际上没有更多要维护的了。
Common.h(将所有内容放在一个文件中)。这通过交叉编织许多无关类型来破坏了可重用性。现在你不能只拿起一个对象并将其移动到另一个项目中。这是不可行的。
基于软件包的依赖关系和包含方式非常好(实际上是理想的)-不要排除这一点。显然,您必须创建设计良好、结构良好且表示相关类组件的包接口(或库)。你正在为对象/组件重用制造一个不必要的问题。最小化库的静态数据,并让链接和剥离阶段完成它们的工作。再次强调,保持您的软件包小而可重用,这将不成问题(假设您的库/软件包经过良好的设计)。
某种花哨的#define魔术,如果没有typedefed就typedef。我对预处理器有一种深深的厌恶,因为我认为它使新手难以理解代码,但也许....
实际上,您可以在同一作用域内多次声明typedef(例如,在两个不同的标头文件中)-这不是错误。
在同一作用域内声明具有不同基础类型的typedef 是错误的。显然。您必须避免这种情况,幸运的是编译器会强制执行此操作。
为了避免这种情况,请创建一个“翻译构建”,其中包括全局信息-编译器将标记不匹配的typedef类型声明。
试图通过最小化typedef和/或前向声明(在编译时足够接近免费)来偷懒是不值得的。有时你需要一堆条件支持前向声明 - 一旦定义了,就很容易了(STL库是一个很好的例子 - 在这种情况下,如果你也在前向声明
template class vector;
)。
最好让所有这些声明可见,以立即捕获任何错误,并且在这种情况下可以避免使用预处理器,这是一个额外的奖励。
使用vector子类而不是typedef。这似乎很危险......
std::vector的子类经常被标记为“初学者的错误”。此容器不应被子类化。不要仅仅为了减少编译时间/依赖性而采用不良实践。如果依赖性真的如此重要,你应该考虑使用PIMPL:
namespace MON {
class FooListPtr;
}
namespace MON {
class FooListPtr {
private:
shared_ptr< vector< shared_ptr<const Foo> > > d_data;
};
}
这里有最佳实践吗?当复用性、可读性和一致性至关重要时,它们在实际代码中的表现如何呢?
最终,我发现一个基于小巧简洁的包的方法最适合复用,可以减少编译时间,同时最小化依赖。