参考了以下两篇文章:

C++编译链接原理简介 

语言程序编译过程 2

问题来源:当模板文件的实现与声明分开在不同文件中时,链接时会提示找不到相应模板函数,如下

一,编译和链接的大概原理:

1,编译,遍历工程的所有代码文件,进行文件分析,这里的分析与文件后缀无关,并不是说以CPP文件为依据,源文件后缀名可以改为任何名字。

编译以文件为单位,将此文件#include的所有文件拿进来,写进此文件中,包含进来的东西可能是函数声明,也可能是函数的实现体。

如果#include "test.h",则包含进来的是一些函数和变量的声明,如果 #include "test.cpp",则其中的函数实现代码也被包含进来了。

编译的结果是一个obj文件,如test.cpp编译后是一个test.obj文件,里面是二进制的汇编指令。

此时,每个编译单元编译完成后,会提供三个表用于后面链接,这三个表分别是【未解决符号表】,【已解决符号表】,【重定向表】

已解决符号表是本编译单元中定义的所有符号,包括变量和函数。

未解决符号表是本编译单元中用到的外部符号,包括变量和函数。

重定向表是用于计算每个编译单元在最终链接完成的EXE中的偏移地址。

2,链接,对所有的obj文件进行拼接。

为什么要拼接?对于每个obj文件,其中若调用了其它文件的函数(外部调用),就需要知道此外部函数的具体实现,这在编译时是不关心的。

这时候去查找所有obj文件的【已解决符号表】中查找此外部函数的实现体,若有两个以上的obj都有此实现,则链接出错,因为函数实现不唯一了,这不允许。此错误就是常见的

XXX 已经在 xxx.obj中定义了,如下:

二,实例分析

1,一个头文件被多个CPP包含时编译链接正确,一个CPP文件被多个其它CPP文件包含时编译正确,链接出错,报错为 XXX 已经在 xxx.obj中定义。

因为CPP中有函数的实现体,每被包含一次就多了一个实现,导致一个函数在不同CPP文件中被多次实现,重复了。

头文件被多次包含为什么没问题?关键是每个头文件开头都有宏 #pragma once,该宏确保了头文件只会被包含一次

2,模板文件的特殊性。

模板文件只有在实例化时才能确定其具体的实现体,所以如果将模板文件的声明和函数体分开在.h和.cpp中,当编译cpp时,并不会产生函数的具体实现体。当在其它文件中#include "template.h"时,会提示找不到函数的定义。

解决方法:在需要使用模板函数的地方,#include "template.cpp",即包含它的CPP文件,而不是.h文件。

原因:使用模板函数的地方,比如 addobj<cube>(),传了具体的模板类型给函数,这样模板函数就能到CPP文件中找到对应的实现体将cube传给模板参数而实例化了。

3,综合的例子,若一个类中既有模板函数,又有非模板函数,那么只能将模板函数的声明与定义写在一个文件中,分开到两个文件是不行的。

-------------------------------------------------------------------------------------------------------------------------------

编译是以CPP为单元进行的,各个编译单元之间相互独立,互不感知。这也是联合联系和多编程编译的基础。
重复包含是什么?
重复包含是一个文件多次包含了另一个文件。
而多个文件都包含了同一个文件不是重复包含。
#pragma once的作用是防止x.h被a.cpp重复包含,而不是防止x.h被b.cpp包含后不再被c.cpp包含

文件 x.h
#pragma once
class CA{};

文件 a.cpp
#include "x.h"
#include "x.h"

文件 b.cpp
#include "x.h"

文件 c.cpp
#include "x.h"

原因要从符号表说起,每个CPP编译时都会产生三个表,【已解决符号表】,【未解决符号表】,【重定向表】
1,如上面a.cpp的情况,包含了两次 x.h,这时候x.h中声明的相关符号就会在【已解决符号表】中重复,于是报错。
2,对于上面 b.cpp, c.cpp的情况,由于二者有相互独立的符号表,所以不会报重,不会报错。
在链接时,是从所有符号表中查找相关符号的定义(实现),符号在多个符号表中声明没关系

C++模板声明与实现分开--由此想到的编译,链接原理的更多相关文章

  1. C++中重定义的问题——问题的实质是声明和定义的关系以及分离式编译的原理

    这里的问题实质是我们在头文件中直接定义全局变量或者函数,却分别在主函数和对应的cpp文件中包含了两次,于是在编译的时候这个变量或者函数被定义了两次,问题就出现了,因此,我们应该形成一种编码风格,即: ...

  2. c++函数模板声明与定义相分离

    最近在仿写stl,发现stl源码中将模板的声明与定义写在一起实在很不优雅.自己尝试用“传统”方法,及在.h文件里声明,在.cpp文件里定义,然后在main函数里包含.h头文件,这样会报链接错误.这是因 ...

  3. C++类模板声明与定义为何不能分开

    我们用C++写类的时候,通常会将.cpp和.h文件分开写,即实现和声明分开写了:但在C++的类模板中,这种写法是错误的. 在<C++编程思想>的第16章的"16.3模板语法&qu ...

  4. 面试问题之C++语言:类模板声明与定义为何不能分开

    C++中每个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的.只有模板被真正使用的时候,编译器才知道,模板 ...

  5. c++模板使用及实现模板声明定义的分离

    c++模板是编译器构造具体实例类型的模型,使类型参数化,是泛型编程的基础,泛型就是独立于特定类型. 一.模板分为函数模板和类模板两种. 函数模板:template <class 形参名,clas ...

  6. 关于C++编译链接和模板函数

    一,关于编译链接编译指的的把编译单元生成目标文件的过程链接是把目标文件链接到一起的过程编译单元:可以认为是一个.c或者.cpp文件.每个编译单元经过预处理会得到一个临时的编译单元.预处理会间接包含其他 ...

  7. 模板函数(template function)出现编译链接错误(link error)之解析

    总的结论:    将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...

  8. C和C指针小记(六)-基本声明、指针声明、typedef 、常量、作用域、链接属性、存储类型、static

    1.变量的声明 声明变量的基本形式: 说明符号(一个或者多个) 声明表达式列表 说明符 (specifier) 包含一些关键字,用于描述被声明的标识符的基本类型,它也可用户改变标识符的缺省存储类型和作 ...

  9. C++/CLI中class成员声明与实现分开在不同文件时必须添加namespace

    以下是我的代码: //TaskConfigFile.h #pragma once using namespace System::Collections::Generic; using namespa ...

随机推荐

  1. yii日志保存机制

    一.修改yii框架的配置文件(main.php) 'log' => array( 'class' => 'CLogRouter', 'routes' => array( array( ...

  2. shell学习笔记3---shell变量

    Shell变量的定义.赋值和删除 脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,Shell 变量也遵循这个规则. 在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有 ...

  3. IntelliJ IDEA中创建Web聚合项目(Maven多模块项目)(转载)

    创建parent项目 1.打开IDEA,注意这里不要勾选模板,用模板创建过maven项目的小伙伴都知道模板创建项目非常慢,所以这里不要选模板,需要的文件夹我们后面自己来创建就可以了.所以这个页面直接点 ...

  4. 安装Pycharm(方便编辑代码的IDE(编辑器))以及 使用Pycharm新建项目

    安装Pycharm(方便编辑代码的IDE(编辑器))以及 使用Pycharm新建项目 一.下载安装Pycharm 首先要下载Pycharm这个软件,官网的下载地址是: http://www.jetbr ...

  5. 标准库path源码解读

    先看标准库 作用:关于路径的一些实用操作 https://github.com/golang/go/blob/master/src/path/path.go 源码地址 func IsAbs func ...

  6. bootstrap table实现iview固定列的效果

    因为bootstrap自带的固定列效果满足不了公司需求,所以借助fixed-table这个插件完成了iview固定列的效果 <!DOCTYPE html> <html lang=&q ...

  7. css厂商前缀

    在vue中写css,不要加厂商前缀,vue-cli会在打包时自动生成

  8. C++ STL(标准模板库)

    一.STL简介 STL(Standard Template Library,标准模板库)是惠普实验室开发的,在被引入C++之前该技术就已经存在了很长的一段时间. STL的代码从广义上讲分为三类:alg ...

  9. dos2unix 将DOS格式转换成NUIX格式

    1.命令功能 dos2unix将windows文件格式转换成unix文件格式. 2.语法格式 dos2unix  file 3.使用范例 [root@localhost ~]# dos2unix wi ...

  10. hdu 4633 Who's Aunt Zhang(polya+逆元)

    Who's Aunt Zhang Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...