首页 > 代码库 > 内部连接和外部连接

内部连接和外部连接


  1. 声明和定义

    声明 是将一个名称引入一个程序.

    定义 提供了一个实体在程序中的唯一描述.

    声明在单个作用域内可以重复多次(类成员除外),定义在一个给定的作用域内只能出现一次. 一个定义就是一个声明,除非:

    • 它定义了类的一个静态数据成员.
    • 它定义了类的非内联成员函数.

  2. 内部连接和外部连接

    当一个实现文件(.cpp ...)编译时,预处理器(CPP)首先递归的包含头文件,形成一个保含有所有必要信息的单个源文件. 这个源文件称为 编译单元.

    内部连接 如果一个名称对于它的编译单元来说是局部的, 并且在连接的时候不可能与其它编译单元中的同样的名称相冲突,则这个名称具有内部连接.即具有内部连接的名称不会被带到目标文件中.

    外部连接 在一个多文件程序中,如果一个名称在连接时可以和其他编译单元交互,那么这个名称就具有外部连接. 即具有外部连接的名称会引入到目标文件中,由连接程序进行处理.这种符号在整个程序中必须是惟一的.

    具有内部连接的定义包括:

    • 加 static 前缀的全局变量定义.如: static int x;
    • 枚举类型的定义.如: enum Boolean {NO,YES };
    • 类的定义. 如: class Point { int d_x; int d_y; ... };
    • 内联函数的定义.如: inline int operator==(const Point& left,const Point&right) { ... }

    具有外部连接的定义包括:

    • 非内联的类成员函数.如: Point& Point::operator+=(const Point& right) { ... }
    • 非内联,非静态的自由函数. 如: Point operator+(const Point& left, const Point& right) { ... }
    • 非静态的全局定义.

    声明本身不会将任何符合引入目标文件,所以声明都是内部连接的.

    某些声明可以激活对一个外部连接定义的访问,也许我们会很随便的说这些声明具有外部连接,如:
          int f();
          extern int i;
          class Point { static int s_numPoints; ... }; /* 类静态数据成员声明具有外部连接, 该数据必须在另一个地方被定义,如: point.c int Point::s_numPoints; */

    还有一些声明即不把什么符号引入目标文件,也不能用来激活对外部连接定义的访问,我们常常称这类声明具有内部连接,
          typedef int INT;
          class Point;
          struct Point;

    把一个带有外部连接的定义放在 .h 文件中都会引起错误.由于类的声明和定义都是内部连接的,一般都放在 .h 文件中.

  3. 验证实例


以下方法验证inline

没加的函数定义默认是外部链接的!
加了 inline 后不管内联是否成功, 函数定义就变成内联的了.
这个很容易实验, 建一个包含两个 cpp 文件的工程, 在这两个文件里都写如下的代码:


C/C++ code
?
1
2
3
4
5
6
7
8
9
inline int x()
{
    return x();
}
 
int y()
{
    return y();
}
  1. 模板特化 头文件中最好使用inline标记来消除重复定义(消除外部连接)


在遇到全特化的时候, 模板函数没有 inline 的话一样会有重定义的问题, 比如下面这样:

test.h
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma once
#include <stdio.h>
 
template <class T> 
class myTemplate
{
public:
    void F1(T& x, T& y);
    void F2(T& x, T& y);
};
 
template<>
void inline myTemplate<int>::F1(int& x, int& y)
{
    int t=x;
    x=y;
    y=t;
}
 
template<>
void myTemplate<int>::F2(int& x, int& y)
{
    int t=x;
    x=y;
    y=t;
}


test1.cpp
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
#include "test.h"
 
int t2()
{
    int x = 0, y = 0;
    myTemplate<int> obj;
    obj.F1(x, y);
    obj.F2(x, y);
    return 0;
}


test2.cpp
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
#include "test.h"
 
int t1()
{
    int x = 0, y = 0;
    myTemplate<int> obj;
    obj.F1(x, y);
    obj.F2(x, y);
    return 0;
}


链接的时候, F1 可以顺利通过, F2 就会报已经定义的错误:
Test2.obj : error LNK2005: "public: void __thiscall myTemplate<int>::F2(int &,int &)" (?F2@?$myTemplate@H@@QAEXAAH0@Z) already defined in TEST.obj


验证实例