现有两个文件Test.h 和Test.cpp
#include 在Test.h中包含 和在Test.cpp中包含有什么区别?
1、在cpp文件中包含.h文件,要么你要用到这个头文件中的函数或者类,要么就是实现这个头文件;
2、.h —就是为了放一堆声明所产生的东西。
如果是定义放在.h中。 如果.h被重复包含多次,那么则会被报重定义。所以在.h 中都要—如果函数就要是inline ,如果是变量就要 selectany (windows)才不会被报错。
3、#include尽量写到cpp文件里。两个文件在.h文件里相互include,就会产生编译错误,而两个文件在.c文件互相include,就不会有该问题,因此在.h文件include就要避免互相包含的问题,而.cpp文件就不需要考虑
4、
1)在 .h 里面 include 的好处是:
如果很多.c,.cpp文件,都包含一批头文件,
如果复制很容易遗漏
如果输入,很容易出错
如果全部在一个.h, include 那么每个.c,.cpp文件只需要一个#include 语句
这样不仅输入量减少,
而且代码也美观多了
代码也主次分明了
毕竟,.c.cpp, 里面
要实现的函数,才是主要代码
2)主要缺陷,
可能会包含完全不需要的头文件,
增加编译工作量
5、如果你在a.h头文件中include了“stdio.h”,“iostream”,……一大堆
那么你的a.cpp源文件只要include你的a.h,就相当于include了“stdio.h”,“iostream”,……一大堆
但是当其他文件include你的a.h的同时也就包含了“stdio.h”,“iostream”,……一大堆
这个要看你个人需要,如果你需要让其他文件也include一大堆,那么写在a.h中就可以,其他文件包含a.cpp简单整洁无脑
如果只有a.cpp需要include一大堆,那么还是建议在a.cpp中include一大堆
6、如果a.c包含了头文件a.h,a.h包含了头文件b.h,b.c也包含了b.h,那么当b.h发生改变时,a.c和b.c都会重新编译
也就是所有包含了b.h的都会重新编译,无论是直接包含,还是间接包含
7、2点原则:
第一个原则:如果可以不包含头文件,那就不要包含了,这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知(C++编译器自上而下编译源文件的时候,对每一个数据的定义,总是需要知道定义的数据的类型的大小)
第二个原则:尽量在CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类 B的前置声明并编译成功,那么在A的实现中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)
C++的类声明、前置声明、定义及各自优势、使用场景
当一个class的成员变量为另外一个类的指针类型的时候,此时,我们可以不在头文件中来包含这个类的头文件,即不使用include“.h”,而是采用前置声明的形式,对这个类进行一个类型的前置声明。
类的前置声明形式上:只有class关键字+类名,后面不带任何符号,即{}
白话解释:哎,b是个类,我要用它,你别管,它在某个地方是个真实的存在(也可能不存在,我就tm忽悠你的), 总之你不要管,我要用它”。这样跟编译器打过招呼之后,就能保证代码编译通过。
如下代码段
//文件a.h
class B;
class A
{
public:
B *b;
};
//文件b.h
class B
{
public:
void func1();
};
a.h
和b.h
这两个文件中定义了两个类分别是A和B。其中A类有一个成员b是Class B的指针,这个时候可使用类前置声明
前置声明省去了#include
的处理、降低文件之间的编译依赖,从而避免不必要的编译时间浪费。
利用前置声明的利弊
优点:避免#include, 避免修改一个被包含的头文件,导致整个文件重新编译。
缺点:(摘自Google)如果前置声明关系到模板,typedefs, 默认参数和 using 声明,就很难决定它的具体样子了。
前置声明作用:
告诉编译器,这个类在其它地方定义,真正使用类的定义的时候才进行编译。
Note:类A在编译的时候不需要拿到类B的定义是因为这里面定义的是指针,而对于指针是不需要定义就可以进行内存布局的,在编译A的类的声明的时候,在进行内存布局的时候是不需要拿到B的定义的
但是在A的成员函数中如果使用这个指针b,则就需要在A对应的.cpp文件中引入B的定义。也就是在.cpp中需要include b.h
这里面对于前置声明,还需要注意一个事情,就是如果另外有一个类,这个类使用了类A,并且通过类A访问使用了A的成员变量b,比如下边这种情况
//文件c.h
#include "a.h"
class C
{
public:
void print(){
a.b->func1();
}
A a;
};
编译时,讲会报错:不允许使用不完全(incomplete)类型A。
Because:C中访问了a成员b的成员函数。所以,此时也需要类B的完整定义,即include
比较正规的做法就是在C的头文件中也引入B的定义,这样编译器就能找到B的定义,从而编译通过
如法1
法1
//文件c.h
#include "a.h"
#include "b.h"
class C
{
...
}
法2
//文件a.h
#include "b.h"
//class B;
class A
{
public:
B *b;
};
法2
在类A的头文件中引入b.h头文件
这种写法,就是放弃了前置声明,直接在A中引入B的头文件,一次性将B的全部声明引入到A的头文件中。
这样也可以编译通过,但是会带来一个问题。就是以后所有引用A的头文件的其他文件,在编译的时候都需要编译一遍B,而在这些文件中可能压根就没有访问成员变量b。也就是说这些文件其实根本就不需要B的定义。这样会导致编译这些文件的时候,还需要编译一遍不相关的类B。这会导致编译缓慢
所以,这里面正确的做法是采用前一种方法。也就是说,头文件中能尽量少的引入头文件就应该尽量少的引用收文件,能用前置声明的就应该尽量使用前置声明
几种可以使用前置声明的 地方
1.以指针或引用的形式来引用类型
class A;
class B
{
A *pa;//ok
A &ra;//ok
A * f(const A * pa);//ok
A & f(const A & pa);//ok
};
2.友元
class A;
class B
{
friend A; //ok
friend class A;//ok
};
3.作为返回值或者参数
class A;
class B
{
void f(A a);//ok
A g();//ok
};
不可以使用前置声明的类型
1.使用完整的类引用
class A;
class B
{
//A a; //error
};
2.被当作父类
class E;
class F: public E //error VC6.0--'E': base class undefined Qt5.6-invalid use of incomplete of 'class C'
{};
使用前置声明和没有使用前置声明的编译过程及其差异
假设有两个类 A 和 B,A 类包含了 B 类的一个指针成员,并且在 A.cpp 文件中使用了 B 类的成员函数。我们将比较两种情况下的编译过程和差异。
情况1:使用前置声明
A.h
#ifndef A_H
#define A_H
class B; // 前置声明
class A {
public:
A();
void doSomethingWithB();
private:
B* ptrB; // 使用B类的指针成员
};
#endif // A_H
A.cpp
#include "A.h"
#include "B.h" // 包含B类的头文件
A::A() {
ptrB = new B(); // 使用B类的指针成员
}
void A::doSomethingWithB() {
ptrB->someFunction(); // 调用B类的成员函数
}
情况2:没有使用前置声明
A.h
#ifndef A_H
#define A_H
#include "B.h" // 直接包含B类的头文件
class A {
public:
A();
void doSomethingWithB();
private:
B* ptrB; // 使用B类的指针成员
};
#endif // A_H
A.cpp
#include "A.h"
A::A() {
ptrB = new B(); // 使用B类的指针成员
}
void A::doSomethingWithB() {
ptrB->someFunction(); // 调用B类的成员函数
}
编译过程及差异:
使用前置声明(情况1):
A.h
中只有 B 类的前置声明,A.cpp
中包含了 A.h
和 B.h
。A.h
时会知道 B 类的存在,但不知道 B 类的具体内容。编译 A.h
时不需要 B 类的完整定义,只需要知道它是一个类的声明即可。A.cpp
中被调用,则编译器需要知道这些函数的声明,因此需要在 A.cpp
中包含 B.h
。A.cpp
,生成目标文件 A.o
。没有使用前置声明(情况2):
-A.h
直接包含了 B.h
,因此 A.h
和 A.cpp
都会有 B 类的完整定义。
A.cpp
时,编译器会将 B 类的完整定义和 A 类的定义都放入编译过程中。B.h
的文件都需要重新编译。A.cpp
,生成目标文件 A.o。差异:
原文链接:https://blog.csdn.net/qq_42681507/article/details/130921276
原文地址:https://blog.csdn.net/xueruifan/article/details/50569639