C++模板声明与实现分开--由此想到的编译,链接原理
参考了以下两篇文章:
问题来源:当模板文件的实现与声明分开在不同文件中时,链接时会提示找不到相应模板函数,如下

一,编译和链接的大概原理:
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++模板声明与实现分开--由此想到的编译,链接原理的更多相关文章
- C++中重定义的问题——问题的实质是声明和定义的关系以及分离式编译的原理
这里的问题实质是我们在头文件中直接定义全局变量或者函数,却分别在主函数和对应的cpp文件中包含了两次,于是在编译的时候这个变量或者函数被定义了两次,问题就出现了,因此,我们应该形成一种编码风格,即: ...
- c++函数模板声明与定义相分离
最近在仿写stl,发现stl源码中将模板的声明与定义写在一起实在很不优雅.自己尝试用“传统”方法,及在.h文件里声明,在.cpp文件里定义,然后在main函数里包含.h头文件,这样会报链接错误.这是因 ...
- C++类模板声明与定义为何不能分开
我们用C++写类的时候,通常会将.cpp和.h文件分开写,即实现和声明分开写了:但在C++的类模板中,这种写法是错误的. 在<C++编程思想>的第16章的"16.3模板语法&qu ...
- 面试问题之C++语言:类模板声明与定义为何不能分开
C++中每个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的.只有模板被真正使用的时候,编译器才知道,模板 ...
- c++模板使用及实现模板声明定义的分离
c++模板是编译器构造具体实例类型的模型,使类型参数化,是泛型编程的基础,泛型就是独立于特定类型. 一.模板分为函数模板和类模板两种. 函数模板:template <class 形参名,clas ...
- 关于C++编译链接和模板函数
一,关于编译链接编译指的的把编译单元生成目标文件的过程链接是把目标文件链接到一起的过程编译单元:可以认为是一个.c或者.cpp文件.每个编译单元经过预处理会得到一个临时的编译单元.预处理会间接包含其他 ...
- 模板函数(template function)出现编译链接错误(link error)之解析
总的结论: 将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...
- C和C指针小记(六)-基本声明、指针声明、typedef 、常量、作用域、链接属性、存储类型、static
1.变量的声明 声明变量的基本形式: 说明符号(一个或者多个) 声明表达式列表 说明符 (specifier) 包含一些关键字,用于描述被声明的标识符的基本类型,它也可用户改变标识符的缺省存储类型和作 ...
- C++/CLI中class成员声明与实现分开在不同文件时必须添加namespace
以下是我的代码: //TaskConfigFile.h #pragma once using namespace System::Collections::Generic; using namespa ...
随机推荐
- [LeetCode] 52. N皇后 II
题目链接 : https://leetcode-cn.com/problems/n-queens-ii/ 题目描述: n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间 ...
- Linux安装 jdk&maven
JDK安装 1. 下载JDK压缩包http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ...
- 使用 ELK 来分析你的支付宝账单
ELK 即 elasticsearch, logstash 以及 kibana.Elasticsearch 是一个基于 lucene 的分布式搜索引擎,logstash 是一种日志传输工具,也可以对日 ...
- java中使用SimpleDateFormat实现字符串和日期的相互转换
java中使用SimpleDateFormat实现字符串和日期的相互转换 import java.text.ParseException; import java.text.SimpleDateFor ...
- CCPC-Wannafly Winter Camp Day1 爬爬爬山 (最短路)
题目描述 爬山是wlswls最喜欢的活动之一. 在一个神奇的世界里,一共有nn座山,mm条路. wlswls初始有kk点体力,在爬山的过程中,他所处的海拔每上升1m1m,体力会减11点,海拔每下降1m ...
- De Moivre–Laplace theorem
网址:https://en.wikipedia.org/wiki/De_Moivre%E2%80%93Laplace_theorem De Moivre–Laplace 中心极限定理的证明.主要用到s ...
- JS监听浏览器标签页的显示与隐藏
/** * 监听浏览器标签页的显示与隐藏 */ class ListenerPageVisibility { constructor () { // 设置隐藏属性和改变可见属性的事件的名称 this. ...
- 【学习】014 深入理解Http协议
Http协议入门 什么是http协议 http协议: 对浏览器客户端 和 服务器端 之间数据传输的格式规范 查看http协议的工具 1)使用火狐的firebug插件(右键->firebug-& ...
- mysql merge引擎分表
---------------------创建表一--------------------------------------DROP TABLE a1;CREATE TABLE `a1` ( `id ...
- 基于iview使用jsx扩展成可编辑的表格
<template> <div> <Table :columns="columns" :data="data"></T ...