利用同名类欺骗C++编译工具链(上)

前言

今天一个老同学帮人做毕设,不过他不熟悉C++,所以来问我,是个简单的二叉树遍历程序。源代码也有,按理说没有任何的难度,整理一下应该就能编译通过了。但是在处理模板类的时候,我按照传统的h声明、cpp实现的方式写了,一编译竟然是链接错误。百度之后发现,C++的类模板并不能像传统的类那样h声明、cpp实现。

模板到底是什么

模板是C++的编译时多态性,这个在学校和面试的时候都已经被问烂了,但是这个编译时多态性到底意味着什么呢?先看看C++源代码是到底是怎么转化为可执行程序的吧。

编译

概括来说编译器会将每个cpp文件翻译成为机器可以识别的机器码,这个过程就是编译。对于C++编译器来说,编译的主体就是cpp文件,每个cpp生成一个目标文件,对于windows来说就是obj,对于linux来说就是o。这些目标文件具有一定的结构,可以让链接器方便的找到各个功能模块(函数)。

首先是预编译,编译器会把所有的预编译命令按照规则进行操作。如#include这个指令就是将include的文件替换到include这个指令的位置,msdn中对include这一指令是这样描述的

#include  "path-spec"  
#include  <path-spec>  

两种语法形式都会导致指令被替换为指定包含文件的整个内容。 两种形式之间的区别在于,在未完全指定路径时预处理器搜索标头文件的顺序。 下表显示了这两种语法形式之间的差异。

也因此重复的包含可能会造成重定义,这个使用现代编译器的#pragma once就可以处理。

链接

然后链接器负责把这些obj和o组合起来,链接成为一个可执行程序。这时链接器需要按照各个目标文件中的功能(函数)调用,把分布在各个obj中的函数整合起来,链接器所使用的函数名称就是各个函数的修饰名,不同编译器不同平台的函数修饰名不同。

编译时多态性

模板的多态是编译时就决定了的,也就是在编译阶段,预编译之后所有的cpp被整合成为一个完整的文件,这里的然后根据该文件中的所有模板调用,为每种模板分别编译生成对应的机器码,例如

template <class T>
class A
{
...
  T a;
  void replace(T t);
...
};

...

A<int> a;
A<char> b;
A<double> c;

这段代码编译器会编译出来三个类,它们的修饰名不同。可以理解为A<int>、A<char>、A<double>类型只是具有相似结构,但是在程序中是独立存在互不相干的三个类。但是这样的类对于文件来说是独立的,也就是说可能A.cpp编译出的obj有这三个类,但是B.cpp编译出来的是完全没有这三个类的。想想如果把replace函数的实现放在单独的cpp中而这些声明放在h文件中会发生什么吧。编译器将h文件中的声明替换到各个include它的地方,然后编译各个cpp文件,假设有如下几个文件

A.h

template <class T>
class A
{
  T a;
  void replace(T t);
};

A.cpp

#include "A.h"
template <class T>
void A<T>::replace(T t)
{
  a = t;
}

main.cpp

#include "A.h"
int main()
{
  A<char> a;
  A<int> b;
  return 0;
}

这样的代码会报数个链接错误,对于A.obj来说,它里面并没有包含任何的replace函数的代码,因为它没有实现任何一个类,即是说模板类本身必须被使用才会被实现。而main.obj里更是没有对replace的实现,因为main.cpp本身就不包含任何replace的代码。链接器执行链接的时候,当然会找不到任何

void A<char>::replace(char t)

void A<int>::replace(int t)

的实现。想要解决也会有两个方案,一个是让replace的实现出现在main.obj中,可以将replace的定义放在A.h中或者main.cpp中,或者是让replace出现在A.obj中,在A.cpp中声明两个类的实现

template class A<char>;
template class A<int>;

或者在A.cpp中使用到实现出来的类。

到此关于模板类的问题解决了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值