我在一个项目中广泛使用 shared_ptr 和 STL,这导致了像 shared_ptr > 这样的过长、容易出错的类型(我是一个 ObjC 程序员,其中长名称是规范,但仍然太过分了。)我相信,始终将其称为 FooListPtr 并记录命名约定“Ptr”表示 shared_ptr 而“List”表示 shared_ptr 的向量会更清楚。
对于初学者,我建议使用良好的设计结构来确定范围(例如命名空间)以及类型定义的描述性、非缩写名称。 FooListPtr 太短了,imo。没有人想猜测缩写是什么意思(或者惊讶地发现 Foo 是 const、shared 等),也没有人想仅仅因为范围冲突而改变他们的代码。
在您的库(以及其他常见类别)中为 typedef 选择前缀也可能会有所帮助。
将类型拖出其声明的范围也是一个坏主意:
namespace MON {
namespace Diddy {
class Foo;
} /* << Diddy */
/*...*/
typedef Diddy::Foo Diddy_Foo;
} /* << MON */
也有例外:
当我们这样做时,应避免命名空间范围和命名空间别名中的using - 如果您想尽量减少未来的维护,请限定范围。
这很容易 typedef,但它会导致标题令人头疼。我似乎有几个定义 FooListPtr 的选项:
Foo.h。这会缠绕所有的标头并造成严重的构建问题,因此它不是首发。
对于真正依赖于其他声明的声明,它可能是一个选项。暗示你需要分包,或者子系统有一个通用的、本地化的接口。
FooFwd.h(“转发标题”)。这是 Effective C++ 建议的,基于 iosfwd.h。这是非常一致的,但是维护两倍数量的标头的开销充其量似乎很烦人。
真的不用担心这个维护。这是一个很好的做法。编译器毫不费力地使用前向声明和 typedef。这并不烦人,因为它有助于减少您的依赖关系,并有助于确保它们都是正确且可见的。由于其他文件引用了“包类型”标头,因此确实不需要维护更多内容。
Common.h(将它们全部放在一个文件中)。这通过缠绕许多不相关的类型而扼杀了可重用性。您现在不能只拿起一个对象并将其移动到另一个项目。这不是首发。
基于包的依赖和包含非常好(非常理想,真的)——不要排除这一点。您显然必须创建设计和结构良好的包接口(或库),并表示相关的组件类。您正在从对象/组件重用中提出不必要的问题。最小化库的静态数据,让链接和剥离阶段完成它们的工作。同样,保持你的包小且可重复使用,这不会是一个问题(假设你的库/包设计良好)。
typedef 的某种花哨的#define 魔法,如果它还没有被 typedefed 的话。我一直不喜欢预处理器,因为我认为它让新人难以理解代码,但也许....
实际上,您可以在同一范围内多次声明 typedef(例如,在两个单独的标头中)——这不是错误。
在同一范围内声明具有不同基础类型的 typedef 是错误。明显地。你必须避免这种情况,幸运的是编译器强制执行。
为避免这种情况,请创建一个包含世界的“翻译构建” - 编译器将标记不匹配的类型定义类型的声明。
试图用最少的 typedef 和/或 forwards(它们足够接近编译时释放)偷偷摸摸是不值得的。有时你需要一堆对前向声明的条件支持——一旦定义,就很容易(stl 库就是一个很好的例子——如果你也前向声明template<typename,typename>class vector;)。
最好让所有这些声明可见以立即捕获任何错误,并且在这种情况下您可以避免使用预处理器作为奖励。
使用向量子类而不是 typedef。这似乎很危险......
std::vector 的子类经常被标记为“初学者的错误”。这个容器不应该被子类化。不要仅仅为了减少编译时间/依赖关系而采取不良做法。如果依赖关系真的那么重要,那么您可能应该使用 PIMPL,无论如何:
// <package>.types.hpp
namespace MON {
class FooListPtr;
}
// FooListPtr.hpp
namespace MON {
class FooListPtr {
/* ... */
private:
shared_ptr< vector< shared_ptr<const Foo> > > d_data;
};
}
这里有最佳实践吗?当可重用性、可读性和一致性至关重要时,它们在实际代码中的表现如何?
最终,我发现了一种基于小型简洁包的方法,最适合重用、减少编译时间和最小化依赖性。