一、功能
1、尾插
2、随机插入
3、随机访问
4、随机查找
5、删除指定下标元素
6、删除指定元素
7、字符串输出
二、功能基类
复习知识:
1、虚函数:实现动态多态,通过派生类的重写实现扩展功能;提高代码的复用性;提高实例化对象的灵活性,使用基类指针或引用指向派生类对象,无需严格控制实例化类型。派生类中可用override标记重写虚函数,使代码更加规范。
2、纯虚函数:实现动态多态的关键;在基类中不提供函数体,直接=0;在派生类中重写纯虚函数,提供统一功能函数接口;
3、抽象类:含有纯虚函数的类叫做抽象类,无法实例化对象;派生类中需要全部重写基类中的纯虚函数,否则也是抽象类。
4、纯虚析构函数:需要类外实现。
5、模板:使用模板功能定义一个基类,接收任何类型的参数;定义一个全局变量作为错误返回值。
#pragma once
/****************************************
* 可变大小容器实现 *
* 基本功能:增、查、删、改 *
* 1、数组实现 *
* 2、链表实现 *
*****************************************/
template<typename T>
T ERROR;
//可变容器功能函数基类模板,提供统一接口
template<typename T>
class MutableContainerFunc {
public:
//在末位添加元素
virtual void add(const T& element) = 0;
//在指定下标添加元素
virtual void add(const T& element, int index) = 0;
//通过下标查找元素
virtual T& getEle(int index) = 0;
//通过元素查找下标
virtual int getIndex(const T& element) = 0;
//通过下标删除元素,返回删除元素值
virtual T remove(int index) = 0;
//通过元素删除元素,返回删除元素值
virtual T removeEle(const T& element) = 0;
//通过下标修改元素
virtual void set(int index, const T& element) = 0;
//升序排序
virtual void sort() = 0;
//转换为字符串
virtual string str() = 0;
//纯虚析构函数
virtual ~MutableContainerFunc() = 0;
};
template<typename T>
MutableContainerFunc<T>::~MutableContainerFunc() {}
三、数组实现
复习知识点:
1、重载:为了解决传入指针参数是指针类型的情况,重载了部分特化函数处理指针;重载了数组运算符[]
2、排序:冒泡排序和插入排序
3、字符串流:包含stringstream头文件,可用ostringstream类构造字符串流,用istringstream类读取字符串流。
#pragma once
#include"MutableContainerFunc.hpp"
#include<iostream>
#include<sstream>
using namespace std;
template<typename T>
class MutableContainerArray :public MutableContainer<T>
{
private:
T* Con_ptr;
size_t Con_len;
public:
MutableContainerArray();
MutableContainerArray(int len);
~MutableContainerArray() override;
//在末位添加元素
void add(const T& element) override;
void add(const T* ptr) override;
//在指定下标添加元素
void add(const T& element, int index) override;
void add(const T* ptr, int index) override;
//通过下标查找元素
T& getEle(int index) override;
//通过元素查找下标
int getIndex(const T& element) override;
int getIndex(const T* ptr) override;
//通过下标删除元素,返回删除元素值
T remove(int index) override;
//通过元素删除元素,返回删除元素值
T removeEle(const T& element) override;
T removeEle(const T* ptr) override;
//通过下标修改元素
void set(int index, const T& element) override;
void set(int index, const T* ptr) override;
//升序排序
void sort() override;
//转换为字符串
string str() override;
//数组符号重载
T& operator[](int index) override;
};
template<typename T>
inline MutableContainerArray<T>::MutableContainerArray()
{
this->Con_len = 0;
this->Con_ptr = NULL;
}
template<typename T>
inline MutableContainerArray<T>::MutableContainerArray(int len)
{
this->Con_len = len;
this->Con_ptr = new T[Con_len];
}
template<typename T>
inline MutableContainerArray<T>::~MutableContainerArray()
{
if (this->Con_ptr) {
delete this->Con_ptr;
this->Con_ptr = NULL;
this->Con_len = 0;
}
}
template<typename T>
inline void MutableContainerArray<T>::add(const T & element)
{
T* temp = new T[Con_len + 1];
for (int i = 0; i < Con_len; i++) {
temp[i] = Con_ptr[i];
}
temp[this->Con_len++] = element;
delete this->Con_ptr;
this->Con_ptr = temp;
}
template<typename T>
inline void MutableContainerArray<T>::add(const T * ptr)
{
add(*ptr);
}
template<typename T>
inline void MutableContainerArray<T>::add(const T & element, int index)
{
if (index >= 0 && index <= this->Con_len) {
T* temp = new T[++this->Con_len];
for (int i = 0, j = 0; i < this->Con_len; i++) {
if (i == index) {
continue;
}
temp[i] = this->Con_ptr[j++];
}
temp[index] = element;
delete this->Con_ptr;
this->Con_ptr = temp;
}
}
template<typename T>
inline void MutableContainerArray<T>::add(const T * ptr, int index)
{
add(*ptr, index);
}
template<typename T>
inline T& MutableContainerArray<T>::getEle(int index)
{
return this->Con_ptr[index];
}
template<typename T>
inline int MutableContainerArray<T>::getIndex(const T & element)
{
int res = -1;
for (int i = 0; i < this->Con_len; i++) {
if (this->Con_ptr[i] == element) {
res = i;
break;
}
}
return res;
}
template<typename T>
inline int MutableContainerArray<T>::getIndex(const T * ptr)
{
return getIndex(*ptr);
}
template<typename T>
inline T MutableContainerArray<T>::remove(int index)
{
T res = ERROR<T>;
if (index >= 0 && index < this->Con_len) {
res = this->Con_ptr[index];
T* temp = new T[this->Con_len - 1];
for (int i = 0, j = 0; i < this->Con_len; i++) {
if (i == index) {
continue;
}
temp[j++] = this->Con_ptr[i];
}
delete this->Con_ptr;
this->Con_ptr = temp;
this->Con_len--;
}
return res;
}
template<typename T>
inline T MutableContainerArray<T>::removeEle(const T & element)
{
T res = ERROR<T>;
int index = getIndex(element);
if (index != -1) {
res = element;
remove(index);
}
return res;
}
template<typename T>
inline T MutableContainerArray<T>::removeEle(const T * ptr)
{
return removeEle(*ptr);
}
template<typename T>
inline void MutableContainerArray<T>::set(int index, const T & element)
{
if (index >= 0 && index <= this->Con_len) {
this->Con_ptr[index] = element;
}
}
template<typename T>
inline void MutableContainerArray<T>::set(int index, const T * ptr)
{
return set(index, *ptr);
}
template<typename T>
inline void MutableContainerArray<T>::sort()
{
for (int i = 0; i < this->Con_len - 1; i++) {
int minindex = i;
for (int j = i + 1; j < this->Con_len; j++) {
if (this->Con_ptr[j] < this->Con_ptr[minindex]) {
minindex = j;
}
}
if (minindex != i) {
T temp = this->Con_ptr[minindex];
this->Con_ptr[minindex] = this->Con_ptr[i];
this->Con_ptr[i] = temp;
}
}
//for (int i = 0; i < Con_len; i++) {
// for (int j = 0; j < Con_len - 1 - i; j++) {
// if (Con_ptr[j + 1] < Con_ptr[j]) {
// T temp = Con_ptr[j];
// Con_ptr[j] = Con_ptr[j + 1];
// Con_ptr[j + 1] = temp;
// }
// }
//}
}
template<typename T>
inline string MutableContainerArray<T>::str()
{
ostringstream oss;
oss << "[";
if (this->Con_len) {
for (int i = 0; i < this->Con_len - 1; i++) {
oss << this->Con_ptr[i] << ",";
}
oss << this->Con_ptr[this->Con_len - 1];
}
oss << "]";
return oss.str();
}
template<typename T>
inline T & MutableContainerArray<T>::operator[](int index)
{
return Con_ptr[index];
}
四、链表实现
复习知识点:
1、大小:size_t 和int ,一般表示数组大小用size_t,防止溢出或者不同平台出现不兼容
2、初始化列表构造:initializer_list<T> 也就是{a,b,c,d…};
#pragma once
#include<iostream>
using namespace std;
#include<sstream>
//声明节点类
template<typename T>
class Node {
public:
T N_value;
Node<T>* pre;
Node<T>* next;
Node();
Node(const T& value);
Node(const T* ptr);
};
//声明链表
template<typename T>
class MutableContainerList :public MutableContainer<T> {
public:
//链表头、尾指针
Node<T>* Con_begin;
Node<T>* Con_end;
//链表长度
size_t Con_len;
//构造、析构函数
MutableContainerList();
MutableContainerList(initializer_list<T> initlist);
~MutableContainerList()override;
//在末位添加元素
void add(const T& element) override;
void add(const T* ptr) override;
//在指定下标添加元素
void add(const T& element, int index) override;
void add(const T* ptr, int index) override;
//通过下标查找元素
T& getEle(int index) override;
//通过元素查找下标
int getIndex(const T& element) override;
int getIndex(const T* ptr) override;
//通过下标删除元素,返回删除元素值
T remove(int index) override;
//通过元素删除元素,返回删除元素值
T removeEle(const T& element) override;
T removeEle(const T* ptr) override;
//通过下标修改元素
void set(int index, const T& element) override;
void set(int index, const T* ptr) override;
//升序排序
void sort() override;
//转换为字符串
string str() override;
//数组符号重载
T& operator[](int index);
//通过下标获得节点指针
Node<T>* getPtr(int index);
};
template<typename T>
inline Node<T>::Node()
{
pre = next = nullptr;
}
template<typename T>
inline Node<T>::Node(const T & value)
{
N_value = value;
pre = next = nullptr;
}
template<typename T>
inline Node<T>::Node(const T * ptr)
{
Node(*ptr);
}
template<typename T>
inline MutableContainerList<T>::MutableContainerList()
{
Con_begin = Con_end = nullptr;
Con_len = 0;
}
template<typename T>
inline MutableContainerList<T>::MutableContainerList(initializer_list<T> initlist)
{
Con_len = 0;
Con_begin = Con_end = nullptr;
for (const T& value : initlist) {
add(value);
}
}
template<typename T>
inline MutableContainerList<T>::~MutableContainerList()
{
while (Con_begin) {
Node<T>* temp = Con_begin->next;
delete Con_begin;
Con_begin = temp;
}
}
template<typename T>
inline void MutableContainerList<T>::add(const T & element)
{
Node<T>* temp = new Node<T>(element);
if (!Con_len) {
Con_begin = Con_end = temp;
}
else {
temp->pre = Con_end;
Con_end->next = temp;
Con_end = temp;
}
Con_len++;
}
template<typename T>
inline void MutableContainerList<T>::add(const T * ptr)
{
add(*ptr);
}
template<typename T>
inline void MutableContainerList<T>::add(const T & element, int index)
{
Node<T>* temp = getPtr(index);
if (temp) {
Node<T> *node = new Node<T>(element);
temp->pre->next = node;
node->pre = temp->pre;
node->next = temp;
temp->pre = node;
Con_len++;
}
}
template<typename T>
inline void MutableContainerList<T>::add(const T * ptr, int index)
{
add(*ptr, index);
}
template<typename T>
inline T & MutableContainerList<T>::getEle(int index)
{
return (*this)[index];
}
template<typename T>
inline int MutableContainerList<T>::getIndex(const T & element)
{
Node<T>* temp = Con_begin;
int i = 0;
for (i = 0; temp->N_value != element && i < Con_len; i++, temp = temp->next);
return i < Con_len ? i : -1;
}
template<typename T>
inline int MutableContainerList<T>::getIndex(const T * ptr)
{
return getIndex(*ptr);
}
template<typename T>
inline T MutableContainerList<T>::remove(int index)
{
Node<T>* temptr = getPtr(index);
T res = temptr->N_value;
temptr->pre->next = temptr->next;
temptr->next->pre = temptr->pre;
delete temptr;
Con_len--;
return res;
}
template<typename T>
inline T MutableContainerList<T>::removeEle(const T & element)
{
return remove(getIndex(element));
}
template<typename T>
inline T MutableContainerList<T>::removeEle(const T * ptr)
{
return removeEle(*ptr);
}
template<typename T>
inline void MutableContainerList<T>::set(int index, const T & element)
{
(*this)[index] = element;
}
template<typename T>
inline void MutableContainerList<T>::set(int index, const T * ptr)
{
set(index, *ptr);
}
template<typename T>
inline void MutableContainerList<T>::sort()
{
for (int i = 0; i < Con_len - 1; i++) {
for (int j = 0; j < Con_len - 1 - i; j++) {
T& valuej = (*this)[j];
T& valuej1 = (*this)[j + 1];
if (valuej > valuej1) {
T temp = valuej;
valuej = valuej1;
valuej1 = temp;
}
}
}
}
template<typename T>
inline string MutableContainerList<T>::str()
{
ostringstream oss;
oss << "[";
for (Node<T>* temp = Con_begin; temp->next; temp = temp->next) {
oss << temp->N_value << ",";
}
oss << Con_end->N_value << "]";
return oss.str();
}
template<typename T>
inline T & MutableContainerList<T>::operator[](int index)
{
Node<T>* temp = Con_begin;
if (index >= 0 && index < Con_len) {
for (int i = 0; i < index; i++, temp = temp->next);
return temp->N_value;
}
else {
return ERROR<T>;
}
}
template<typename T>
inline Node<T>* MutableContainerList<T>::getPtr(int index)
{
Node<T>* temp = nullptr;
if (index >= 0 && index < Con_len) {
temp = Con_begin;
for (int i = 0; temp&&i != index; i++, temp = temp->next);
}
return temp;
}
五、我要认真了
小一:背景
写代码的时候我钻进了死胡同,分开解释。
第一种情况:T是基础类型或者自定义基础类型,在数组和链表析构时,不需要特殊处理。
第二种情况:T本身是指针类型,而且指向堆区空间,在数组和链表析构时,如果不释放掉就会造成内存泄漏。
小二:那么问题来了:
众所周知(装一下,其实我也是刚知道),C++是一种静态类型的编程语言,所有类型在编译阶段就已经确定,那么我们的模板中,该如何判断传入的参数是什么类型呢?
小三:感谢文心一言:
抓破脑袋想不出办法的时候,文心一言靠谱了一回。他说条件编译和类型萃取(不明觉厉),然后我就恶补一下。enable_if 和 is_pointer 登场。
1、类型萃取:编译阶段判断类型,主要通过模板实现,提前优化或者排错。头文件<type_traits>,看源码是一个一路继承下来的类模板,T是指针时继承了true_type,返回1;T不是指针时,继承了false_type,返回0;在<type_traits>文件中还有多种模板,判断类型例如:
is_class<T> :判断是否是自定义类型
is_reference<T>:判断是否为引用
is_integral<T>:判断是否为整形
…………等等
//判断是否为指针类型,返回bool值
is_pointer<T>::value
2、条件编译:C++的条件编译主要依赖于预处理器指令,这些指令在编译过程的预处理阶段被处理。常见的条件编译指令包括#if
、#ifdef
、#ifndef
、#elif
、#else
和#endif
等。而enable_if是实现类模板内成员函数条件编译的一个模板类,他通过对一个布尔条件的判断确定是否返回类型补齐模板,条件为true返回指定类型,补齐模板,通过编译;当条件为false,则不返回任何东西,模板实例化失败,跳过后续的类模板或者函数模板的编译。
template<typename = typename enable_if<true,void>::type>;
代码中第一个typename 是用来声明模板参数的。它告诉编译器接下来的名字,此处是匿名;
代码中第二个typename 用来告诉编译器,enable_if<…>::type 是一个类型
它通过在函数模板的参数列表中添加一个额外的模板参数,并结合类型萃取技术,实现对函数模板的条件编译。源码如下:
template<bool _Test,
class _Ty = void>
struct enable_if
{ // type is undefined for assumed !_Test
};
template<class _Ty>
struct enable_if<true, _Ty>
{ // type is _Ty for _Test
using type = _Ty;
};
template<bool _Test,
class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;
小四:那么问题是否解决了呢?
并没有, 当我试图使用条件编译来编写针对指针数据特化版本的成员函数时发现,函数模板实例化是在编译时进行的,而虚函数是在运行时绑定的。意思也就是:虚函数不能是函数模板。那么与其让多态和继承让步于条件编译,不如不用条件编译。
而且:重点是,我一直想把数据中内存维护的任务加到容器中,其实是强“容”所难了,容器做好自身的内存维护就好,至于数据所指向的内存,应该由数据本身或者操作者去维护。所以,将模拟容器中对指针数据的特化版成员函数中的指针表示为:T*,从而实现特化,程序也就更加清晰。事实上,模板元编程中也是这么处理的。例如:
//指针类型判断
template<class T>
struct is_pointer<T*>:true_type
{
};
小结:程序设计要保持清晰的条理性,遵循客观规律。