一、前言
最近在看《大规模C++程序设计》一书,看第一章关于内部链接和外部链接这部分时,有点不太明白。通过书本理解和网上查阅文献,在此记录一下自己对这部分知识点的理解。
首先,提几个问题:
- 什么是内部链接,什么是外部链接?
- 为什么不要在头文件中定义具有外部链接的实体?
- 在头文件中定义具有内部链接的实体有什么劣势?
- 内部链接和外部链接存在的意义是什么?
二、编译单元
上图简单的表述了程序的编译、链接过程。其实编译器在编译代码时,只会去编译.cpp格式的源文件,并且预编译器会递归的把.cpp中所有#include的头文件都“拷贝”到.cpp文件中去,将#include替换成对应头文件中的内容,形成一个完整的.cpp文件,之后再对这个文件进行编译,生成二进制的.obj文件,所以其实每个.cpp文件就是一个编译单元。
每个编译单元由编译器独立编译,编译完成后,链接器会将编译后的编译单元合并到单个程序中!!!
三、声明和定义
3.1、声明
描述:一个声明将一个名称引入一个作用域
声明可以理解为,我现在声称有这个东西,但是这个东西具体是啥样,我不清楚。
- C++中在同一个作用域中可以重复声明,除了类中的成员函数与成员变量的声明
以下都是声明:
extern int number; //外部引用声明
typedef int int32; // typedef声明
Class A; //类的前置声明
Using std::cin; //名字空间引用声明
friend f; //友元声明
int testFun(); //函数前置声明
3.2、定义
描述:定义决定了一个实体在一个作用域的唯一描述
定义可以这么理解,声明是声称有这个东西,但是具体是啥样还不清楚;定义就清清楚楚、明明白白的告诉你,这个东西是啥样。
- 同一作用域不可以重复定义一个实体
以下都是声明:
int a; //变量定义
Class Myclass{…}; //类定义
Myclass ma; //类对象定义
static int b; //静态变量定义
enum{first, second,third}; //枚举类型定义
const int m = 2; //常量定义
void hello(){…} //函数定义
四、内部链接
描述:如果一个名称对于它的编译单元是局部的,并且在链接时不会与其它的编译单元中同样的名字冲突,那么这个名称就拥有内部链接。这个实体有内部链接,它就不会与其他.cpp文件同名的实体冲突。
以下实体拥有内部链接:
- 静态全局变量定义
- 自由函数(非类成员函数)定义
- 友元函数定义
- 类的定义
- 内联函数定义
union
共用体定义- 名字空间的const常量定义
enum
枚举类型定义- 所有的声明
五、外部链接
描述:一个多文件的程序中,一个实体可以在链接时与其它编译单元交互,那么这个实体就拥有外部链接。也就是说,如果该编译单元能向其它编译单元提供其定义,供其它编译单元使用的函数、变量就拥有外部链接。
以下实体拥有外部链接:
- 类的非内联函数的定义(包括:成员函数和静态类成员函数)
- 类的静态成员变量的定义
- 名字空间或全局的非静态的自由函数、非静态变量、非友元函数的定义
六、内部链接和外部链接的意义
链接:就是因为项目工程的不断扩大,写在一个.cpp文件即难以维护,又不好去合作开发。所以去将代码按照比较有条理的,分成多个文件,让其可以独立编译,最后运行时再整合到一起,也就是通过链接去找到需要的代码。这时候就需要外链接定位到合适的代码。
比如:我们定义的全局函数和变量,可以跨模块的链接使用。
有一些名字定义所表示的实体拥有外部链接,这样就意味着他可以跨越编译单元去进行代码的链接。所以,拥有外部链接的实体如果被声明在头文件,并且被多个.cpp文件包含,可能就会出现链接冲突错误。因为每个包含这个拥有外部链接实体的.cpp都会分配空间,当多个编译单元链接的时候,连接器就会面对多个相同的名字,无法正常链接到正确的对象。
例如:我们在一个头文件中定义了一个名字空间,名字空间的定义显然具有外部链接
#ifndef LESSON_H
#define LESSON_H
namespace lesson_1 {
int test;
}
class lesson
{
public:
lesson();
};
#endif // LESSON_H
#include "lesson.h"
lesson::lesson()
{
}
然后我们在main.cpp中引入:#include "lesson.h"
#include <iostream>
#include "lesson.h"
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
一运行就报错:
因为定义在lesson.h
中的namespace lesson
具有外部链接,lesson.cpp
和main.cpp
都#include<lesson.h>
,在链接时,这两个编译单元,拥有同名的外部链接,程序就不知道怎么去把编译单元拼接起来,所以报错:重复定义lesson_1::test