• C++模板声明与实现分开--由此想到的编译,链接原理


    参考了以下两篇文章:

    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的情况,由于二者有相互独立的符号表,所以不会报重,不会报错。
    在链接时,是从所有符号表中查找相关符号的定义(实现),符号在多个符号表中声明没关系

  • 相关阅读:
    洛谷1069 细胞分裂
    洛谷1050 循环
    CF Good Bye 2018
    洛谷1043 数字游戏
    洛谷1041 传染病控制
    洛谷1040 加分二叉树
    洛谷1039 侦探推理
    洛谷1038 神经网络
    设计模式的区别
    volatile和synchronized与lock的理解
  • 原文地址:https://www.cnblogs.com/timeObjserver/p/11338982.html
Copyright © 2020-2023  润新知