问题01:如何确保头文件只包含了一次
在你的头文件中使用 #define 来定义一个宏,并且在没有这个宏被定义时包含这个头文件中的内容。你可以使用 #ifndef、#define 和 #endif 预处理指令集合。
- #ifndef MYCLASS_H__
- #define MYCLASS_H__
- // Put everything here ...
- #endif
问题02:如何保证一个跨多个源文件的变量只有一个实例存在
在一个单一的实现文件中以一种通常的方式来声明和定义这个变量,并且在其他的实现文件中使用extern关键字来访问这个变量。关键字extern告诉编译器这个变量的存储空间已经在别的地方分配。关键字extern也告诉链接器它查验的这个变量存在与别的目标文件中,并且当链接器创建最后的可执行文件或库时,他需要到别的文件中找到这个变量。如果链接器永远都不能找到你声明的这个外部变量,或者它找到这个变量的两个或两个以上的定义,那么就会报一个链接错误。
问题03:当你引用了别的头文件中的类,如何减少编译依赖
在可能需要避免不必要的编译依赖的地方使用前向类声明。一个前向类声明是一个忽略细节的方法,而这些细节又是你不需要关心的(即你不需要调用该类的方法或访问该类的成员变量)。
如果你在一个头文件Myclass.h中使用#include来包含其他类的头文件的话,你可能需要使用六、七个类的头文件。一旦它们中的任何一个有更新,那么所有包含Myclass.h的实现文件不得不重新编译。
问题04:如何避免不同模块中的变量名产生冲突
使用命名空间(namespace)来模块化代码。使用命名空间,你就能吧一些大的分布在多个独立文件中的代码组成到一个单一的命名空间中。并且你也可以依据需要来嵌套使用命名空间来把一个大的模块分成多个子模块,并且你的模块的用户就可以有选择地使用他们需要的存在于你的命名空间中的元素。
使用命名空间别名可以避免输入冗长的路径。
- using dev = handware::devices;
- dev::Device d;
但是你需要明智地使用命名空间,下面是当你需要使用命名空间时的一些流行指导意见:
>> 保守地使用using namespace xxx。引入一个完整的命名空间增加了名字冲突的可能,并且 这样也降低了有命名空间提供的模块性。
>> 在头文件不用使用using语句。头文件被很多其他文件所包含,因此如果你在一个头文件中使用一个命名空间或者命名空间的东西时,你就给任何包含这个头文件的文件暴露了你正在使用的任何东西。解决这个问题的方法是在这个头文件中使用完整的名字路径来限制它。
>> 在#include指令前面不要使用using声明或者定义。如果你这样做的话,在这个头文件中你就会暴露你正在使用的任何东西,这肯定不是这个头文件作者的本意。
问题05:假如你有很多需要变成内联函数的成员函数或者独立函数,如何保证声明和实现分离
创建一个.inl文件,并且在你的头文件的尾部使用#include来包含它。这样完成的功能和你吧函数的定义放在头文件的尾部是等价的。
- // Value.h
- #ifndef VALUE_H__
- #define VALUE_H__
- #include <string>
- class Value {
- public:
- Value(const std::string& val) : val_(val) {}
- std::string getVal() const;
- private:
- std::string val_;
- };
- #include "Value.inl"
- #endif VALUE_H__
- //Value.inl
- inline std::string Value::getVal() const { return(val_); }