栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

将文件间的编译依存关系降至最低

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

将文件间的编译依存关系降至最低

结论:避免大量依赖性编译的解决方案是,在头文件中用 class声明 外来类,用指针或引用来代替变量的声明;在CPP文件中包含外来类的头文件。

好处:

如果A.h发生改变,所有包含类A对象的文件都要重新编。在B.cpp中包含A.h的好处是:所有用到类B对象的文件都不用重新编译。

// A.h
class A{
	funcA();
};

// A.cpp
A的实现

// B.h
class A;
class B{
    A* xx;   //用A的指针来替代变量的声明
};
// B.cpp
#include"A.h"
A.funcA();

背景:

假设有三个类ComplexClass, SimpleClass1和SimpleClass2,采用头文件将类的声明与类的实现分开。

这样共对应于6个文件,分别是ComplexClass.h,ComplexClass.cpp,SimpleClass1.h,SimpleClass1.cpp,SimpleClass2.h,SimpleClass2.cpp。

ComplexClass复合两个baseClass,SimpleClass1与SimpleClass2之间是独立的,ComplexClass的.h是这样写的:

#ifndef COMPLESS_CLASS_H
#define COMPLESS_CLASS_H

#include “SimpleClass1.h”
#include “SimpleClass2.h”

class ComplexClass
{
    SimpleClass1 xx;      // SimpleClass1类做变量
    SimpleClass2 xxx;     // SimpleClass2类做变量  
};

#endif 
考虑下面几种情况: Case 1:

SimpleClass1.h发生了变化,比如添加了一个新的成员变量。请问ComplexClass需要重编吗?

需要。因为ComplexClass.h里包含了SimpleClass1.h(使用了SimpleClass1作为成员对象的类)。而且所有使用ComplexClass类的对象的文件,也都需要重新编译!

Q1:如果把#include “SimpleClass1.h”换成类的声明class SimpleClass1;,能通过编译 吗?

不能。因为编译器需要知道ComplexClass成员变量SimpleClass1对象的大小,而这些信息仅由class SimpleClass1;声明是不够的。

#ifndef COMPLESS_CLASS_H
#define COMPLESS_CLASS_H
 
#include “SimpleClass2.h”

class SimpleClass1;

class ComplexClass
{
    SimpleClass1 xx;      // SimpleClass1类做变量
    SimpleClass2 xxx;     // SimpleClass2类做变量  
};

#endif 

Q2:如果将SimpleClass1作为一个函数的形参,或者是函数返回值,用class SimpleClass1声明就够了。

1 // ComplexClass.h
  
2 class SimpleClass1;
3 …
4 SimpleClass1 GetSimpleClass1() const;    //SimpleClass1为函数的返回值
5 …

Q3:声明class SimpleClass1,然后将SimpleClass1作为指针也 可以通过编译。

因为编译器把所有指针看作一个字长(32位的机器为4 Byte)。

但如果要想使用SimpleClass1的方法,还是要包含SimpleClass1.h,但那是ComplexClass.cpp做的,因为ComplexClass.h只负责类变量和方法的声明。

 1 // ComplexClass.h
   
 2 #include “SimpleClass2.h”
 3 
 4 class SimpleClass1;
 6 class ComplexClass: 
 7 {
 8     SimpleClass1* xx;        //SimpleClass1作为指针
 9     SimpleClass2 xxx;
10 };
Case 2:

SimpleClass1.cpp发生了变化,比如改变了一个成员函数的实现逻辑,但SimpleClass1.h没有变。那么SimpleClass1一定会重编,SimpleClass2因为独立性不需要重编,ComplexClass 需要重编 吗?

不需要。因为编译器重编的条件是发现一个变量的类型或者大小跟之前的不一样了,但现在SimpleClass1的接口并没有任务变化,只是改变了实现的细节,所以编译器不会重编。

Case 3:

结合Case1和Case2,现在我们来看看下面的做法:

// ComplexClass.h
#include “SimpleClass2.h”

class SimpleClass1;

class ComplexClass
{
    SimpleClass1* xx;
    SimpleClass2 xxx;
};


// ComplexClass.cpp

void ComplexClass::Fun()
{
    SimpleClass1->FunMethod();
}

请问上面的ComplexClass.cpp 能通过编译 吗?

否。因为这里用到了SimpleClass1的具体的方法,所以需要包含SimpleClass1的头文件,但这个包含的行为已经从ComplexClass里面拿掉了(换成了class SimpleClass1),所以不能通过编译。

如何解决这个问题呢?

只要在ComplexClass.cpp里面加上#include “SimpleClass1.h”就可以了。

换言之,我们其实做的就是将ComplexClass.h的#include “SimpleClass1.h”移至了ComplexClass1.cpp里面,而在原位置放置class SimpleClass1。

这么做的目的是为了什么?假设这时候SimpleClass1.h发生了变化,会有怎样的结果呢?

SimpleClass1自身一定会重编,SimpleClass2当然还是不用重编的;

ComplexClass.cpp因为包含了SimpleClass1.h,所以需要重编,但换来的好处就是所有用到ComplexClass的其他地方,它们所在的文件不用重编了!因为ComplexClass的头文件没有变化,接口没有改变!

总结:

对C++类而言,如果它的头文件(.h)变了,那么这个类的对象所在的文件都有重编。

但如果它的实现文件(.cpp)变了,而头文件(.h)没有变(对外的接口没有变),那么这个类的对象所在的文件都不会因之重编。

因此,避免大量依赖性编译的解决方案就是:在头文件中用class声明外来类,用指针或引用代替变量的声明;在cpp文件中包含外来类的头文件。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/768515.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号