优缺点
优点
1.c++前置声明,可以节约预处理器的展开时间,也就是在编译的时候速度是增快了,但是伴随着很多坑。
2.当被前置声明的类改动后,只需要编译包含改动类头文件的源文件,所有使用了前置声明的源文件不需要改动
体现
1.所有引用TestB.h 的其他 .cpp文件不用再去包含 tem_A 与 Util 这俩个类的头文件,这俩个类的文件TestB.cpp 会包含。其他的.cpp 文件只需链接TestB.o 文件即可。这样有多少个使用到Test_B的源文件就会省多少次相应的头文件展开。
2. 如果Util 与 tem_A 的头文件发生变化,只需要Test_B 重新编译即可,其他用到Test_B的源文件不需要重新编译,只需链接新的Test_B.o 即可。
#ifndef TEST_B_H
#define TEST_B_H
template<class T>
class tem_A;
class Util;
class B {
public:
B()
{}
private:
tem_A<Util>* handle;
};
#endif //TESTB_H
缺点
1.可能引发bug
struct B {};
struct D : B {};
// good_user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); } // calls f(B*)
//good_user.cc:
struct D;
void f(B*);
void f(void*);
void test(D*x)
{
f(x); // call f(void*) 因为前置声明并不能体现出继承关系所以重载的认为void*更匹配发生了隐式转换。
}
2.该类型的名字被修改,我们需要修改所以使用到它的源文件。
3.std内的类使用前置声明可能有未定义行为
4.标准C++的规范并没有定义enum结构的大小。故即使在头文件对enum进行前置声明,在引用enum的时候,还是无法确定enum的大小,故无法对enum进行前置声明。
前置声明的使用
前置声明只能使用于 指针、引用、函数形参、函数返回值。如果用于类内部的成员变量的声明,类的继承列表、STL容器的模板参数都是非法的。
关于模板参数是可以使用前置声明的,但是如果delete 前置声明类型的指针是未定义行为。
错误1 前置声明成员类成员变量的声明
A.h
class A {
int a_;
};
B.h
class A;
class B {
void Fun();
A a;
}
B.cpp
#include "A.h"
void B::Fun()
{
a.a_++;
}
main.cpp
#include "B.h"
int main()
{
B b;
return 0;
}
g++ -o main.o -c main.cc testb.h:7: error: field 'a_' has incomplete type 错误
错误2前置声明用于继承
A.h
class A {
int a_;
};
B.h
class A;
class B:public A {
void Fun();
}
B.cpp
#include "A.h"
void B::Fun()
{
a.a_++;
}
main.cpp
#include "B.h"
int main()
{
B b;
return 0;
}
testb.h:4: error: invalid use of incomplete type 'struct A'
testb.h:2: error: forward declaration of 'struct A'
错误3 STL容器类模板参数
A.h
class A {
int a_;
};
B.h
class A;
class B {
void Fun();
vector<A> a;
}
B.cpp
#include "A.h"
void B::Fun()
{
xxxxxx;
}
main.cpp
#include "B.h"
int main()
{
B b;
return 0;
}
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_vector.h: In destructor 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp = A, _Alloc = std::allocator<A>]':
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_vector.h:208: instantiated from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = A, _Alloc = std::allocator<A>]'
testb.h:4: instantiated from here
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_vector.h:132: error: invalid use of incomplete type 'struct A'
testb.h:2: error: forward declaration of 'struct A'
前置声明几种常见的使用方法
前置声明某个具体的类
A.h
class A{
};
B.h
class A;
前置声明某个typedef 的类型
A.h
class objcet{
};
typedef object defobject;
B.h
class object;
typedef object defobject;
或者
typedef class object defobject;
前置声明某个作用域内的变量
A.h
namespace Test{
class A{
};
}
B.h
namespace Test{
class A;
}
Pimpl
pimpl 优化。这个是用在前置声明具体的一个的实现。比如如下场景它里面有三个具体的成员, std_class , std_class_b , define_class 这三个是无法直接前置声明的。必须在相应的头文件中 包含这三个类的头文件。
a.b
class Test {
void Fun () {
a.fun();
b.fun();
c.fun();
}
std_class a;
std_class_b b;
define_class c;
};
使用 pimpl 优化就可以起到前置声明的效果。如下面的优化这样 A.h 就不用包含相应头文件了。
a.b
#include <memory>
class Test {
public:
void Fun();
private:
class impl;
std::unique_ptr<impl> pimpl_;
};
a.cc
class Test::impl {
void Fun () {
a.fun();
b.fun();
c.fun();
}
std_class a;
std_class_b b;
define_class c;
};
// class test
void Test::Fun() {
pimpl_->Fun();
}
总结
为了编译效率可以想其他办法,好好使用include就行。