深入理解C++内链接与外链接的意义

上一篇博客给大家解释了“程序运行链接”的概念与意义,并区分了动态链接库与静态链接库。接下来想和大家谈一下C++的内链接与外链接的区别与意义。看完之后,希望你能理解以下几个问题~

1.      为什么不要在头文件中定义具有外部链接的实体?

2.      在头文件中定义具有内部链接的实体有什么劣势?

3.      内部链接与外部链接存在的意义是什么?

首先理解什么是编译单元?

我们知道,其实编译器在编译代码时,只会去编译.cpp格式的源文件,并且预编译器会递归的把.cpp所有#include的头文件都“拷贝”到.cpp文件中去,之后对这个文件再进行编译,生成二进制的.obj文件。那么其实每一个.cpp文件都是一个编译单元。

声明与定义

一个声明将一个名称引入一个作用域,C++中在同一个作用域中可以重复声明,除了类中的成员函数与成员变量的声明。以下都是声明:

Extern int number; //外部引用声明

Typedef int  int32; // typedef声明

Class A;          //类的前置声明

Using std::cin;   //名字空间引用声明

Friend f;         //友元声明

Int testFun();    //函数前置声明

定义决定了一个实体在一个作用域的唯一描述,同一作用域不可以重复定义一个实体。以下都是定义:

Int a;

Class Myclass{…};

Myclass ma;

Static int b;

Enum{first, second,third};

Const int m = 2;

Void hello(){…}

什么是内部链接?

如果一个名称对于他的编译单元是局部的,并且在链接时不会与其他的编译单元中同样的名字冲突,那么这个名称就拥有内部链接。这个实体有内部链接,他就不会与其他.cpp文件同名的实体冲突。换个说法,那些编译单元(.cpp)中不能向其他编译单元(.cpp)展示提供其定义的函数、变量就拥有内部链接

那么哪些实体拥有内部链接?

1.      静态(static)全局变量,自由函数,友元函数定义

2.      类的定义

3.      内联函数定义

4.      Union共用体定义

5.      名字空间的const常量定义

6.      枚举类型定义

7.      所有的声明(有人将声明归结为无链接)

什么是外部链接?

一个多文件的程序中,一个实体可以在链接时与其他编译单元交互,那么这个实体就拥有外部链接。

换个说法,那些编译单元(.cpp)中能想其他编译单元(.cpp)提供其定义,让其他编译单元(.cpp)使用的函数、变量就拥有外部链接

那么哪些实体拥有外部链接?

1.      类的非内联函数(包括成员函数和静态类成员函数)的定义

2.      类的静态成员变量的定义

3.      名字空间或全局的非静态的自由函数,非静态变量,非友元函数的定义

那么这里总结一下,定义这样的内链接与外链接有什么意义?

所谓链接,就是因为项目工程的不断扩大,写在一个.cpp文件即难以维护,又不好去合作开发。所以去将代码按照比较有条理的,分成多个文件,让其可以独立编译,在最后运行在整合到一起,也就是通过链接再去找到需要的代码。这时候就需要外链接定位到合适的代码。

比如我们定义的全局函数和变量,可以跨模块的链接使用。

有一些名字定义所表示的实体拥有外部链接,这样就意味着他可以跨越编译单元去进行代码的链接。所以,拥有外部链接的实体如果被声明在头文件并且被多个.cpp文件包含,可能就会出现链接冲突错误,因为每个包含这个拥有外部链接实体的.cpp都会分配空间,当多个编译单元链接的时候,连接器就会面对多个相同的名字,无法正常链接到正确的对象。

下面举个例子:(VS2012环境下)

//lesson.h

namespace lesson

{
         int  test;

}

//lesson.cpp

#include "stdafx.h"

#include "lesson.h"

int _tmain(intargc,_TCHAR*argv[])

{
         system("pause");

         return0;

}

//test.cpp

#include "lesson.h"

我们就会看到

error LNK2005: "intlesson::test" (?test@lesson@@3HA) 已经在 lesson.obj 中定义C:\Users\user\Documents\Visual Studio 2012\Projects\lesson\lesson\stdafx.obj

这样的错误提示。

而对于拥有内部链接的实体则不会出现这样的情况,因为他不会与其他.cpp的同名实体产生冲突。比如我们将上面的lesson.h改为

//lesson.h

class lesson

{
         int  test;

}

这样就不会有任何错误,因为类的定义是有内部链接的。

如果在lesson.h里面再定义静态变量,枚举类,进行各种声明等,这些实体由于有内部链接所以仍然是合法的,编译器会认为你想在各个编译单元中都有一个私有的副本。

那么进一步的概括这些内容就是一句话

相同作用域内的声明可以有多个,但是只能定义一次。

先不考虑内链接还是外链接,我们都知道一个{}里面不可能定义两个一模一样的名字。对于一个单独的.cpp文件,我们是知道的,但是对于多个文件,好像就稍微有点晕。其实,这是一个道理,我们的外部链接就是让各个.cpp文件能链接到一起,这样在.cpp文件遇到第一个{}之前,他们的作用域就可以理解为相同的,所以拥有外部链接的实体(全局函数,变量等)出现在第一个{}之前,而且名字相同,那就是出现了定义重复的错误。

我们再看,所有的声明都是有内部链接的,然而他其实可以链接到其他文件,因为他的定义是在其他的编译单元的,所以多个编译单元拥有相同的声明也是合理的。但是,我们知道,这个声明对应的定义肯定只有一个。

最后再给出一个C++编程建议,慎重考虑在头文件中定义有链接的实体

一,如果头文件是像int a=1;这样的定义,被包含在多个.cpp文件后肯定会报出链接错误。

二,如果是想static int a = 2;这样的定义就会在所有包含他的.cpp文件中生成一个副本,如果被大量源文件include的话,就会占据大量的空间,造成内存浪费。

总结

到此这篇关于C++内链接与外链接意义的文章就介绍到这了,更多相关C++内链接与外链接内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++静态链接与动态链接详解

    目录 一.GCC工作流程 二.静态链接与动态链接 1.静态链接 2.动态链接 总结 一.GCC工作流程 预处理:把#头文件展开,进行宏替换,去掉注释(生成.i文件) 编译:把预处理后的文件生成汇编文件(.s文件),主要是检查语法.语义问题 汇编:把汇编文件生成目标文件(.o文件) 链接:将函数库中相应的代码组合到目标文件,生成可执行文件(默认a.out文件) o文件不会立即执行,因为可能出现:一个.cpp文件中的函数引用了另一个.cpp文件中定义的符号/调用了某个库文件中的函数.链接的目的就是将

  • 深入理解C++内链接与外链接的意义

    上一篇博客给大家解释了"程序运行链接"的概念与意义,并区分了动态链接库与静态链接库.接下来想和大家谈一下C++的内链接与外链接的区别与意义.看完之后,希望你能理解以下几个问题~ 1.      为什么不要在头文件中定义具有外部链接的实体? 2.      在头文件中定义具有内部链接的实体有什么劣势? 3.      内部链接与外部链接存在的意义是什么? 首先理解什么是编译单元? 我们知道,其实编译器在编译代码时,只会去编译.cpp格式的源文件,并且预编译器会递归的把.cpp所有#inc

  • 用 JavaScript 给站外链接的 cursor 进行改造

    用 JavaScript 给站外链接的 cursor 进行改造 - Beautiful Style « 样式之美 » loaoao.com /* * Author:aoao * Homepage:http://www.loaoao.com * Email:loaoao@gmail.com / QQ:2222342 * Copyright (c) 2006 aoao * Licensed under a Creative Commons Attribution 2.5 License (http:

  • 深入理解JavaScript内置函数

    javascript函数一共可分为五类: · 常规函数 · 数组函数 · 日期函数 · 数学函数 · 字符串函数 1.常规函数 javascript常规函数包括以下9个函数: (1)alert函数:显示一个警告对话框,包括一个OK按钮. (2)confirm函数:显示一个确认对话框,包括OK.Cancel按钮. (3)escape函数:将字符转换成Unicode码. (4)eval函数:计算表达式的结果. (5)isNaN函数:测试是(true)否(false)不是一个数字. (6)parseF

  • 详解MySQL数据库--多表查询--内连接,外连接,子查询,相关子查询

    多表查询 使用单个select 语句从多个表格中取出相关的查询结果,多表连接通常是建立在有相互关系的父子表上; 1交叉连接 第一个表格的所有行 乘以 第二个表格中的所有行,也就是笛卡尔积 创建一个消费者与顾客的表格: 代码如下: -- create table customers( -- id int primary key auto_increment, -- name VARCHAR(20)not null, -- address VARCHAR(20)not NULL -- ); -- C

  • php实现首页链接查询 友情链接检查的代码

    复制代码 代码如下: <?php /* * 网站首页超链接反查 友情链接查询 外链查询 * web技术爱好者 lost63.com原创 QQ:35501547 * 转载请注明出处 */ if($_GET['action']=="check"){ $domain=$_POST['domain']; //域名表单项为空,则获取地址栏参数 if($domain==""){ $domain=$_GET['domain']; } //网址 $url="http

  • postgreSQL中的内连接和外连接实现操作

    测试数据: city表: create table city(id int,name text); insert into city values(0,'北京'),(1,'西安'),(2,'天津'),(3,'上海'),(4,'哈尔滨'),(5,'西藏') person表: create table person(id int,lastname char(20)); insert into person values(0,'Tom'),(2,'Lily'),(3,'Mary'),(5,'Coco'

  • SQL语句的并集UNION 交集JOIN(内连接,外连接)等介绍

    1. a. 并集UNION SELECT column1, column2 FROM table1 UNION SELECT column1, column2 FROM table2 b. 交集JOIN SELECT * FROM table1 AS a JOIN table2 b ON a.name=b.name c. 差集NOT IN SELECT * FROM table1 WHERE name NOT IN(SELECT name FROM table2) d. 笛卡尔积 SELECT

  • IOS 长链接与短链接之间的转换

    IOS 长链接与短链接之间的转换 首先需要将字符串使用md5加密,添加NSString的md5的类别方法如下 .h文件 #import <CommonCrypto/CommonDigest.h> @interface NSString (md5) -(NSString *) md5HexDigest; @end .m文件 #import "NSString+md5.h" @implementation NSString (md5) - (NSString *) md5Hex

  • 用shell脚本实现自动切换内网和外网实现高可用

    首先说明下我们的配置文件,都是类似格式的,假如内网是192.168.0.3,外网是123.123.123.123,配置文件如下: $db['salver']['hostname'] = '192.168.0.3:3306';//$db['salver']['hostname'] = '123.123.123.123:3306'; 这个时候就是外网是注释的状态.走的是内网.说下思路,就是一个配置文件,复制出来2分放到别的地方,这两份一个里面设置的是内网,一个里面设置的是外网,如果内网不通的情况下,

  • 浅谈CMake配置OpenCV 时静态链接与动态链接的选择

    方法: 添加OpenCV_STATIC 选项,设置为不勾选,在cmake配置的时候就会选择动态库 否则,cmake 配置的时候会设置为静态库 以上这篇浅谈CMake配置OpenCV 时静态链接与动态链接的选择就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

随机推荐