欢迎光临散文网 会员登陆 & 注册

2 引入模板,支持复杂类型

2023-03-06 15:05 作者:HC_0702  | 我要投稿

本项目GitHub: HuangCheng72/HCSTL: 我的STL实现 (github.com): https://github.com/HuangCheng72/HCSTL

进入正文。

我们在上一篇已经实现了只支持double类型的vector,那么问题来了,如果我们要实现支持C++全部内建类型的vector,该怎么做?我们可以考虑全都重新实现一遍,但这样工作量显然是太大了。所以我们需要C++的模板机制,把vector变成一个模板类。内建类型,很好实现,直接加上模板语句 template 开文本替换用 T (其实你自己可以指定任意名字,Tp,E什么的都行)全部替换一下double就行了:

但是问题来了,如果不是内建类型的数据呢?比如用户自己定义的类的对象这种数据,它和内建类型的数据有什么区别?

大家都上过C++课,也都应该知道,类的对象是new出来的,而内建类型的数据是可以不用new的(要是非要new那我也没话说)。

而new一个类的对象,涉及了两个操作:

  1. 申请一块内存空间,用来存放这个对象。

  2. 在这块空间上根据参数输入调用类相应的构造函数,创建这个对象(初始化内存空间的各个参数)。

在我们上面的代码中,我们可以看到,在vector的构造函数和辅助函数中,我们都已经申请了一段内存空间(数组)用来存放vector中的数据。我们可以有两种存放对象的方法:

  1. 将vector的数组所存放数据的类型改为T类型的指针,然后每个元素都是一个对象的指针,对象直接new出来。

  2. 将对象在vector的数组空间上创建。

为了与其他类型相统一,我们采用第二种方法,第一种方法如果感兴趣的话可以自行尝试,本教程对此不讨论。

针对第二种方法,C++有一种运算符,placement new(定位new),它的作用是在指定的内存空间上创建对象,用法如下:

我们可以考虑采用定位new,来实现我们的方法,将对象在vector的数组空间上创建。

以带参构造函数为例:

那么就引出一个问题,我们该怎么判断呢?

我们自然而然可以想到,能不能实现一个判断的函数,这个函数输入的参数是一种类型,返回一个bool值,如果输入的参数是C++内建类型,则返回true,如果输入的参数不是C++内建类型,则返回false,这样是不是能够解决问题了?

但是很遗憾,C++的函数参数不能是一种类型。当然,如果你要将类型名字转化为字符串,然后用字符串进行判断也是可以的,不过本教程对此不讨论。

STL 对此采取的方法是类型萃取(type_traits),它的意义就是将类型的信息提取出来,可以通过一个判断机制进行判断,达到我们的目的。类型萃取的实现采用了模板特化的思路。

在这里需要介绍一下POD类型:

POD(Plain Old Data)类型,指的是C++的内建数据类型,还有原生指针和C风格的结构体联合体等。标准的定义是:能用C的memcpy()等函数进行操作 的类、结构体或联合体。POD类型有以下标准:

  1. 没有用户自定义的构造函数、析构函数、拷贝赋值运算符;

  2. 没有虚函数和虚基类;

  3. 所有非静态成员都是public;

  4. 所有非静态成员都是POD类型;

  5. 没有继承或只继承了POD类型。

POD类型和复杂数据类型最大的区别是四个方面:构造(默认构造器)、析构、赋值、复制(拷贝构造器),即default_constructor,destructor,assignment_operator,copy_constructor。 复杂数据类型先天缺少这四样(编译器给它加上的不算),而可以认为POD类型是先天具备或者 根本不用考虑 这四样。

请新建一个头文件为 type_traits.h ,在这个文件中实现类型萃取。

首先用两个空结构体表示true和false的结果(不用bool类型的原因是因为空结构体不占用内存空间,bool变量还占用1字节空间):

然后利用C++的模板参数特化,将这两个结构体转化为bool值可以用于判断。

这是类型萃取的模板类:

这是针对内建类型的特化实现:

类型萃取这部分使用结构体模板实现,最大的好处是空模板不占用内存空间(如果使用bool变量必然要占用至少一个字节的内存空间),同时结构体类型还可以作为模板参数传递,还有就是C风格结构体也是POD,这样就不用管构造和析构的事情了。

所以,通过类型萃取,我们就可以写我们的判断条件了,还是以之前的带参构造函数为例,可以写成:

因此,我们可以实现支持POD和非POD(non-POD)类型数据的vector了。

修改main.cpp,尝试运行一下non-POD类型的简单测试:

但是我们会发现运行不了。我这里的IDE报错为:

通过查询,该错误代码 0xC0000374 的含义为 A heap has been corrupted ,即堆内存损坏。

debug发现,问题出在辅助函数这里。

这是怎么回事呢?

分析代码上下文我们发现了一个问题。

这就出现了歧义,编译器不知道你要销毁的是到底是哪个,贸然销毁导致堆内存损坏。这个问题在析构函数中也是一样存在的。

但是我们暂时没有好的解决方法,所以先搁置这个问题,加一个判断,先run起来再说

run起来,可以看到main.cpp中我们的简单测试顺利过关。

欢迎访问本项目的GitHub仓库,如果对您有帮助,麻烦给项目一个star,谢谢!

HuangCheng72/HCSTL: 我的STL实现 (github.com): https://github.com/HuangCheng72/HCSTL

2 引入模板,支持复杂类型的评论 (共 条)

分享到微博请遵守国家法律