前向声明
- 使用方法:在头文件中仅声明类,不包含类的完整定义,在cpp文件中再包含完整定义头文件。例如:
// forward_declaration.h
class MyClass; // 前向声明
class AnotherClass {
MyClass* myClassPtr; // 可以使用指针或引用
};
// forward_declaration.cpp
#include "forward_declaration.h"
#include "my_class.h" // 包含MyClass完整定义
// AnotherClass的实现
- 优点:
- 提高编译效率:减少了头文件之间的依赖,当被前向声明类的定义发生变化时,引用该前向声明的文件无需重新编译,只需要重新编译包含该类完整定义的文件。
- 降低耦合度:头文件对其他类的依赖减少,使得代码结构更加清晰,维护性增强。
- 缺点:
- 功能受限:只能使用该类的指针或引用,不能声明该类的对象,因为编译器不知道类的大小。
- 不适用于复杂类型:如果类涉及模板、虚函数等复杂特性,前向声明可能无法满足需求,需要完整定义。
内联函数
- 使用方法:在类定义中直接定义函数,编译器会尝试将函数体嵌入到调用处,减少函数调用开销。例如:
class MyClass {
public:
inline int getValue() { return value; }
private:
int value;
};
- 优点:
- 提高运行效率:减少函数调用的开销,如栈的开辟与销毁、参数传递等,对于短小频繁调用的函数效果显著。
- 代码简洁:函数定义与使用紧密结合,增强了代码的可读性和维护性。
- 缺点:
- 增加代码体积:如果函数体较大,内联会导致代码膨胀,增加内存占用。
- 编译时间变长:编译器需要处理更多的代码,可能导致编译时间增加,尤其是在大型工程中。
预编译头文件
- 使用方法:将工程中频繁使用且不常改变的头文件(如标准库头文件)放在一个预编译头文件(如
stdafx.h
)中,在其他源文件中只需包含该预编译头文件。例如:
// stdafx.h
#include <iostream>
#include <vector>
// main.cpp
#include "stdafx.h"
// 主函数代码
- 优点:
- 显著提高编译效率:预编译头文件只编译一次,后续源文件引用时直接使用预编译结果,大大减少编译时间。
- 缺点:
- 维护成本:如果预编译头文件中的内容频繁变动,预编译的优势会减弱,且需要重新编译预编译头文件。
- 不灵活:对工程结构有一定要求,添加或移除预编译头文件中的头文件需要谨慎处理。
分离模板定义
- 使用方法:将模板类或函数的声明放在头文件,定义放在单独的cpp文件,但通过
export
关键字(C++11之前)或显式实例化(C++11及之后)来让编译器知道模板的定义。例如(C++11之后显式实例化):
// template_class.h
template <typename T>
class TemplateClass {
public:
void print(T value);
};
// template_class.cpp
#include "template_class.h"
#include <iostream>
template <typename T>
void TemplateClass<T>::print(T value) {
std::cout << value << std::endl;
}
// 显式实例化
template class TemplateClass<int>;
// main.cpp
#include "template_class.h"
int main() {
TemplateClass<int> tc;
tc.print(10);
return 0;
}
- 优点:
- 保持代码结构清晰:模板声明和定义分离,与普通类的声明定义模式类似,提高代码维护性。
- 提高编译效率:减少头文件内容,避免模板定义在每个包含头文件的地方都实例化,降低编译时间。
- 缺点:
- 语法复杂:显式实例化需要明确指定模板参数类型,对于大量不同类型的使用不太方便。
- 兼容性问题:
export
关键字在不同编译器上支持不一致,C++11后移除,导致旧代码移植可能存在问题。