C++类模板实战之手写精简版vector容器,详解版

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: C++类模板实战之手写精简版vector容器,详解版

案例要求


可以对内置数据类型以及自定义数据类型的数据进行存储

将数组中的数据存储到堆区

构造函数中可以传入数组的容量

提供对应的拷贝构造函数以及operator=防止浅拷贝问题

提供尾插法和尾删法对数组中的数据进行增加和删除

可以通过下标的方式访问数组中的元素

可以获取数组中当前元素个数和数组的容量


完成步骤


1、封装数组类属性并完成有参构造以及析构函数

#pragma once
#include<iostream>
using namespace std;
template<class T>
class Arrays
{
private:
  T* arr;//数组arr存放T类型的数据
  int capacity;//数组容量
  int size;//数组大小
public:
  Arrays(int capacity)
  {
  this->capacity = capacity;
  this->size = 0;
  this->arr = new T[this->capacity];
  }
  ~Arrays()
  {
  if (this->arr != NULL)
  {
    delete []this->arr;
    this->arr = NULL;
  }
  }
};


       我把自己的这个数组类模板放到一个.hpp文件里,方便测试的时候调用。代码第一行是为了防止头文件重复包含,template里面的T就是数组的数据类型,根据调用时不同的指定存放不同类型的数据。将数组arr以及数组容量和大小进行封装,写在私有权限下即可 。然后提供该类的有参构造,参数列表传入的是数组容量,有参构造初始化了数组的容量以及大小并将数组开辟到了堆区。析构函数就是来清理堆区数据,如果我们开辟的堆区数组不为空,那就清理掉并将其指向NULL,这样可以防止野指针出现,避免异常。


2、提供对应的深拷贝构造函数防止调用析构时出错


Arrays(const Arrays& p)
  {
  this->capacity = p.capacity;
  this->size = p.size;
  this->arr = new T[p.capacity];
  for (int i = 0; i < this->size; i++)
  {
    this->arr[i] = p.arr[i];
  }
  }


       如果不提供深拷贝,那么编译器就会有:this->arr=p->arr 这行代码 ,那么一旦我们调用编译器提供的浅拷贝,当运行到析构函数时,就会出现重复删除地址的情况,必然会出现程序错误。所以我们要自己提供深拷贝构造函数,将上面的代码改为 this->arr= new T[p.capacity] ,这样调用析构的时候各自删除各的堆区数据,不会出现上述情况。最后利用for循环将传进来的对象的数据赋值给新开辟的数组。


3、重载类内的赋值运算符防止浅拷贝问题出现


 

Arrays& operator=(const Arrays& p)
  {
  if (this->arr!=NULL)
  {
    delete []this->arr;
    this->arr = NULL;
    this->capacity = 0;
    this->size = 0;
  }
        //深拷贝过程
  this->capacity = p.capacity;
  this->size = p.size;
  this->arr = new T[this->capacity];
  for (int i = 0; i < p.size; i++)
  {
    this->arr[i] = p.arr[i];
  }
  return *this;
  }


       当数组数据是对象的类型时,不能简单的将数组进行赋值操作,因为也牵扯到直接赋值出现一样的数组地址的情况,存在着深浅拷贝问题。赋值的时候是将传入参数的数据赋值给自己,因此先把自己的属性清空,然后就是深拷贝的实现了。最后返回的是*this,this指针能够指向不同成员属性,那么*this就是对象本身,然后看到返回值类型是对象引用,这样就可以实现对象间的连续赋值了。


4、提供尾部插入和删除的方法


 

void insert_Arrays(const T&value)
  {
  if (this->capacity == this->size)
  {
    return;
  }
  this->arr[size] = value;
  this->size++;
  }
  void delete_Arrays()
  {
  if (this->size == 0)
  {
    return;
  }
  this->size--;
  }


       尾插过程:先判断数组是否已经满了,如果不满就将形参赋值给当前数组最后一个下标的位置,然后更新数组下标,这样就能保证每次插入的数据都在数组末尾。


       尾删的实现:先判断数组是否为空,不为空的时候直接把数组大小减一即可,让编译器访问不到当前的最后一个数组元素。注意尾删的只是数据的指针,数组的地址并未删除。


5、重载[]得到数组中对应下标的数据信息


T& operator[](int index)
  {
  return this->arr[index];
  }


      如果数组内容是对象类型,是不存在对象数组的,所以要对[]运算符进行重载。返回值类型为数据类型的引用,也就是具体的数组内的值,传进去的整型参数就是数组下标。


6、提供get方法获取当前数组容量及大小


 

int getSize()
  {
  return this->size;
  }
  int getCapacity()
  {
  return this->capacity;
  }


       这里就是经典的get方法了,返回对应封装的成员属性 ,不做多解释。


7、提供打印函数测试基本数据类型和自定义数据类型的存储


#include"arrays.hpp"
class Hero
{
  friend void printHero(Arrays<Hero>&hero);
private:
  string name;
  string position;
public:
  Hero() {}
  Hero(string name, string position)
  {
  this->name = name;
  this->position = position;
  }
};
void printArrays(Arrays<int>arr)
{
  for (int i = 0; i < arr.getSize(); i++)
  {
  cout << arr[i] << " ";
  }
  cout << endl;
}
void printHero(Arrays<Hero>&hero)
{
  for (int i = 0; i < hero.getSize(); i++)
  {
  cout << "姓名:"<<hero[i].name<<" 位置:"<<hero[i].position<<endl;
  }
}
void test()
{
  cout << "普通类型数组测试:" << endl;
  cout << "输入数组容量为:" ;
  int n = 0; cin >> n;
  Arrays<int> array(n);
  cout << "输入数据:";
  for (int i = 0; i < array.getCapacity(); i++)
  {
  int value = 0;
  cin >> value;
  array.insert_Arrays(value);
  }
  cout << "打印数组信息:"<<endl;
  printArrays(array);
  array.delete_Arrays();
  cout << "删除一次尾部数据后打印数组信息:" << endl;
  printArrays(array);
}
void test1()
{
  cout << "自定义类型数组测试:" << endl;
  Hero h1("火舞","中单");
  Hero h2("韩信","打野");
  Hero h3("桑启","游走");
  Hero h4("守约","发育");
  Hero h5("关羽","对抗");
  Arrays<Hero> array(5);
  array.insert_Arrays(h1);
  array.insert_Arrays(h2);
  array.insert_Arrays(h3);
  array.insert_Arrays(h4);
  array.insert_Arrays(h5);
  printHero(array);
  array.delete_Arrays();
  cout << "删除一次尾部数据后打印数组信息:"<<endl;
  printHero(array);
}


       首先引入之前封装的数组类头文件,提供printArrays和printHero函数来进行数组信息的打印,test和test1函数分别是整型数组和对象数组的测试。接下来看运行效果。


运行效果:


相关文章
|
26天前
|
Java 应用服务中间件 Linux
【Docker容器化技术】docker安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库
本文主要讲解了Docker的安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库以及Docker容器虚拟化与传统虚拟机比较。
323 11
【Docker容器化技术】docker安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库
|
10天前
|
Kubernetes Linux 开发者
深入探索容器化技术——Docker 的实战应用
深入探索容器化技术——Docker 的实战应用
40 5
|
9天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
24 2
|
15天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
45 5
|
21天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
55 4
|
22天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
52 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
18 1
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4