C++泛型编程(模板)实践
引言
💡 作者简介:专注于C/C++高性能程序设计和开发,理论与代码实践结合,让世界没有难学的技术。包括C/C++、Linux、MySQL、Redis、TCP/IP、协程、网络编程等。
👉
🎖️ CSDN实力新星,社区专家博主
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu
🔔 上一篇:【035】C++泛型编程(模板)之 类模板详解(最全讲解)
🔔 上一篇:【037】读懂C++的强制类型转换static_cast、const_cast、dynamic_cast以及reinterpret_cast
一、类模板的概述
C++ 类模板是一种用于创建通用类的机制,它可以让程序员编写一次类,然后让它适用于多种类型,在实际编程中非常实用。
类模板和函数模板的定义和使用类似。有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,类模板用于实现类所需数据的类型参数化。
类模板的定义方式类似于普通类的定义,只是需要在类名后面添加一对尖括号,其中包含类型参数列表。
设计一个数组类模板,可以存放任意数据类型。
二、实现数组类模板
类模板一般在hpp
文件里面实现。由于数组类模板要存放任何数据类型,所以先定义一个模板类型T
。
Data.hpp
#pragma once
#ifndef _DATA_H_
#include <string.h>
#include <iostream>
using namespace std;
// 类模板
template <class T>
class MyArray {
template<class T1>
friend ostream& operator<<(ostream &out, MyArray<T1> ob);
private:
T *arr;//数组首地址
int size;//实际大小
int capacity;//总容量大小。
public:
MyArray();
MyArray(int capacity);
MyArray(const MyArray &ob);
~MyArray();
MyArray& operator=(MyArray &ob);
void pushBack(T elem);//插入元素
void sortArrary();//排序
};
#endif // !_DATA_H_
template<class T>
MyArray<T>::MyArray()
{
capacity = 5;
size = 0;
arr = new T[capacity];
memset(arr, 0, sizeof(T)*capacity);
}
template<class T>
MyArray<T>::MyArray(int capacity)
{
this->capacity = capacity;
size = 0;
arr = new T[capacity];
memset(arr, 0, sizeof(T)*capacity);
}
template<class T>
MyArray<T>::MyArray(const MyArray & ob)
{
this->capacity = ob.capacity;
this->size = ob.size;
this->arr = new T[this->capacity];
memset(this->arr, 0, sizeof(T)*this->capacity);
memcpy(this->arr, ob.arr, sizeof(T)*this->capacity);
}
template<class T>
MyArray<T>::~MyArray()
{
if (arr != NULL)
{
delete[] arr;
arr = NULL;
}
}
template<class T>
MyArray<T> & MyArray<T>::operator=(MyArray<T> & ob)
{
// 判断 this->arr是否存在空间
if (arr != NULL)
{
delete[] arr;
arr = NULL;
}
// TODO: 在此处插入 return 语句
this->capacity = ob.capacity;
this->size = ob.size;
this->arr = new T[this->capacity];
memset(this->arr, 0, sizeof(T)*this->capacity);
memcpy(this->arr, ob.arr, sizeof(T)*this->capacity);
return *this;
}
template<class T>
void MyArray<T>::pushBack(T elem)
{
// 判断容器是否满
if (size == capacity)
{
// 扩容
capacity = 2 * capacity;
// 申请临时空间
T *tmp = new T[capacity];
if (arr != NULL)
{
// 将旧空间数据拷贝到新空间
memcpy(tmp, arr, sizeof(T)*size);
// 释放旧空间
delete[] arr;
}
// arr指向新空间
arr = tmp;
}
arr[size] = elem;
size++;
}
template<class T>
void MyArray<T>::sortArrary()
{
if (size == 0)
{
cout << "容器没有数据" << endl;
return;
}
// 冒泡排序
for (int i = 0; i < size - 1; i++)
{
for (int j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
T tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
template<class T1>
ostream& operator<<(ostream &out, MyArray<T1> ob)
{
for (int i = 0; i < ob.size; i++)
{
out << ob.arr[i] << " ";
}
out << endl;
return out;
}
main.cpp
#include <iostream>
#include <string>
using namespace std;
class Person {
// 重载<<运算符
friend ostream& operator<<(ostream &out, Person ob);
private:
int num;
string name;
float score;
public:
// 必须要有无参构造
Person(){
}
Person(int num, string name, float score)
{
this->num = num;
this->name = name;
this->score = score;
}
// 重载>运算符才能正确比较
bool operator>(Person &ob)
{
return num > ob.num;
}
};
ostream& operator<<(ostream &out,Person ob)
{
out << ob.num << " " << ob.name << " " << ob.score << endl;
return out;
}
#include "Data.hpp"
int main()
{
// 类模板实例化对象
MyArray<int> arr01;
arr01.pushBack(20);
arr01.pushBack(60);
arr01.pushBack(40);
cout << arr01 << endl;
arr01.sortArrary();
cout << arr01 << endl;
MyArray<char> arr02;
arr02.pushBack('A');
arr02.pushBack('H');
arr02.pushBack('D');
cout << arr02 << endl;
arr02.sortArrary();
cout << arr02 << endl;
// 对象的存储
MyArray<Person> person;
person.pushBack(Person(102,"hello",92.30f));
person.pushBack(Person(108, "world", 95.30f));
person.pushBack(Person(103, "lests", 98.30f));
person.sortArrary();
cout << person << endl;
return 0;
}
三、类模板的继承
3.1、类模板派生出普通类
在类模板派生处具体化为普通类。
示例:
#include <iostream>
using namespace std;
template<class T1,class T2>
class Base {
private:
T1 a;
T2 b;
public:
Base() {}
Base(T1 a, T2 b);
void showData();
};
template<class T1, class T2>
Base<T1, T2>::Base(T1 a, T2 b)
{
this->a = a;
this->b = b;
}
template<class T1, class T2>
void Base<T1, T2>::showData()
{
cout << a << " " << b << endl;
}
// 在类模板派生处具体化为普通类
class Son :public Base<int, char> {
public:
int c;
public:
Son(int a, char b, int c) :Base<int, char>(a, b) {
this->c = c;
}
};
int main()
{
Son ob(100, 'A', 200);
ob.showData();
return 0;
}
3.2、类模板派生出类模板
抽象化类型。
示例:
#include <iostream>
using namespace std;
template<class T1,class T2>
class Base {
private:
T1 a;
T2 b;
public:
Base() {}
Base(T1 a, T2 b);
void showData();
};
template<class T1, class T2>
Base<T1, T2>::Base(T1 a, T2 b)
{
this->a = a;
this->b = b;
}
template<class T1, class T2>
void Base<T1, T2>::showData()
{
cout << a << " " << b << endl;
}
// 在类模板派生类模板
template<class T1, class T2,class T3>
class Son :public Base<T1, T2> {
public:
T3 c;
public:
Son(T1 a, T2 b, T3 c) :Base<T1, T2>(a, b) {
this->c = c;
}
};
int main()
{
Son ob(100, 'A', 200);
ob.showData();
return 0;
}
四、总结
C++ 的标准库中提供了 std::vector
容器,它是一个动态数组,能够在运行时根据需要动态调整大小。如果想要实现一个类似于 std::vector
的容器,需要考虑以下几个要点:
-
动态内存管理:类似于
std::vector
,你需要使用动态内存分配来管理存储元素的内存。可以使用new
和delete
或者malloc
和free
等来实现。 -
大小和容量管理:你的容器应该具有类似于
std::vector
的size()
和capacity()
方法,用于获取当前存储元素的数量和容器的容量。当容量不足时,需要进行内存重新分配来扩大容量。 -
元素访问和操作:你的容器应该提供类似于
std::vector
的push_back()
、pop_back()
、at()
、front()
、back()
等方法来访问和操作容器中的元素。还需要实现类似于std::vector
的迭代器来遍历容器。 -
内存管理和异常安全:你需要确保容器在内存管理和异常安全方面与
std::vector
一致。例如,在内存重新分配时,需要正确地处理内存释放和重新分配,以及在发生异常时确保容器的状态不会被破坏。 -
复制和移动语义:你的容器应该具有正确的复制和移动语义,包括拷贝构造函数、拷贝赋值运算符、移动构造函数和移动赋值运算符等。
-
内存回收:当容器不再需要时,需要正确地释放内存,避免内存泄漏。可以在容器的析构函数中进行内存释放。
总结起来,实现一个类似于 std::vector
的容器需要考虑动态内存管理、大小和容量管理、元素访问和操作、内存管理和异常安全、复制和移动语义以及内存回收等方面。这涉及到动态内存分配、指针和引用、构造函数和析构函数、运算符重载等 C++ 的一些基本概念和技术。在实际编程中,可以参考 std::vector
的实现和相关的 C++ 标准库文档来更深入地了解和实现类似的容器。