模板如何做到声明和定义分离
  YGRwB9iV7sL2 2023年11月02日 39 0

首先我们在之前学习模板的时候说过,在使用模板的时候我们要做到声明和定义不分离,但是这是为什么呢?如果模板的声明和定义分离又会发生什么事情呢?

模板如何做到声明和定义分离_实例化

这是声明

模板如何做到声明和定义分离_实例化_02

这是定义


在进行编译之后,可以看到报了下面的错误

模板如何做到声明和定义分离_编译器_03

可以看到这是一个编译错误,那么如果我将这个函数模板替换成一个普通函数呢?

模板如何做到声明和定义分离_类模板_04

普通函数的声明

模板如何做到声明和定义分离_类模板_05

普通函数的定义

模板如何做到声明和定义分离_编译器_06

重新编译运行都是成功的。

那么这是为什么呢?

我们首先要知道编译器底层是怎么处理我们写的代码的

模板如何做到声明和定义分离_实例化_07

结合到我们刚刚写的函数,在第一部的时候,会将Stack.h中的iostream展开,同时在test.cpp和stack.cpp中将stack.h展开

然后第二步就是构成汇编代码。那么汇编在test.cpp中发现了两个函数调用

模板如何做到声明和定义分离_编译器_08

然后编译器就会生成一个汇编指令,上图中的注释和,其中的问好也就是这个函数定义的地址,因为需要这个函数的地址所以编译器会从这里往上去寻找,是否存在这样的函数。但是在这个stack.h和stack.cpp中都没有定义,但是具有声明,所以编译器会暂时让这里通过。如果连声明都没有就会直接报错。等到编译器对所有文件都进行了汇编操作之后,就到链接操作了。对于func函数在链接的时候,在stack.cpp中发现了func的函数地址,所以这里可以通过,,但是因为我们的Add函数模板并没有实质的生成一个Add函数(不知道T的类型),所以在链接的时候,编译器没有发现Add函数的函数地址,最后就报错了。为什么会出现这样的问题呢?归根到底,是因为编译器在汇编的时候是针对于每一个文件单独的进行汇编的,所以对于Stack.cpp来说并不知道要实例化的T类型,所以没有实例化成具体的函数,最后导致出错。

模板如何做到声明和定义分离_类模板_09

这也正是模板不能支持分离编译的原因。那么如何解决这个问题呢?

我们知道这里问题的所在就是在Stack.cpp中没有实例化Add函数,即在stack.h里面我知道Add函数要实例化成什么,但是没有定义,在Stack.cpp那里我有Add定义但是我不知道要实例化成什么。

那么解决这个问题的第一个方法也就是让在stack.cpp中的Add模板显示实例化。

模板如何做到声明和定义分离_实例化_10

由此可以知道模板不是真的不能声明和定义分离,你只要显示实例化了就可以。但是这样写有一个很大的缺陷,那就是如果我在主函数中将参数修改了呢?

那么为了不报错我就必须在显示实例化一个函数模板。以上的所有对于一个类模板也是成立的。如果我想要将一个类模板声明和定义分离,那么第一个解决方法也就是显示实例化。只不过类模板的显示实例化,只用写一个就能够实例化类里面所有的函数了。


那么除了这个方法之外呢?还有一个方法那就是我们删除stack.cpp函数,然后在stack.h文件里面,将声明放到上面,然后在下面完成完成对函数的定义。

模板如何做到声明和定义分离_类模板_11

所以如果我们要做到模板的声明和定义分离,那么可以使用的方法也就是那两个(对于类模板和函数模板都是这样)。

下面是一个类模板的定义:


模板如何做到声明和定义分离_类模板_12

类模板的声明

模板如何做到声明和定义分离_类模板_13

这是类模板的显式实例化

模板如何做到声明和定义分离_实例化_14

而对于类模板要做到长远一点的解决方法也是一样的将声明放到上面,将定义放到下面。


下面我们在来总结一下使用模板的缺陷和优点:

模板如何做到声明和定义分离_编译器_15

希望这篇博客能对你有所帮助,如果发现了任何错误欢迎指出。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
YGRwB9iV7sL2