C++ 在.h文件中包含头文件和在.cpp文件中包含头文件有什么区别?前置声明
现有两个文件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 类的完整定义,只需要知道它是一个类的声明即可。 - 如果 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 类的头文件发生变化,会导致包含了
B.h
的文件都需要重新编译。 - 编译器将编译
A.cpp
,生成目标文件 A.o。
- 在编译
差异:
- 使用前置声明:减少了对 B 类的完整定义的依赖,只需要类的声明即可,从而减少了编译时间。但需要在使用 B 类成员函数时,在 A.cpp 中包含 B.h。
- 没有使用前置声明:导致 A.h 中包含了 B.h 的完整定义,增加了对 B 类的依赖,编译时间可能更长,并且如果 B.h 发生变化,可能会导致更多文件重新编译。
原文链接:https://blog.csdn.net/qq_42681507/article/details/130921276
原文地址:https://blog.csdn.net/xueruifan/article/details/50569639
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!