c++前置声明与头文件引用

优缺点

优点

  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 tem_A;

class Util;

class B { 
 public:
  B() 
  {}  

 private:
  tem_A* 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;
}
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]':
/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]'
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 
class Test {
public:
void Fun();   
private:
class impl;
std::unique_ptr 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就行。

你可能感兴趣的:(编程心得)