C++泛化动态数组

泛化动态数组

动态数组的核心思想是在存储数据时动态的管理数组元素占用的内存,通过调用动态数组的类方法来对数组中的数据进行增删改查操作。最初我们为数组申请10个元素的空间,放我们不断向数组中添加数据时,会自动的申请更大的空间(这里申请的是原本容量的1.5倍,用的是移位操作),将原来内存块的数据拷贝到新的更大的内存块中。当我们删除数据,当空闲内存是总容量的一半且不小于最小容量(10)时,将自动对数组内存缩容操作。

抽向数据类型(ADT) :(linearList.h)

/*************************************************************************
> File Name       : linearList.h
> Author          : Harold
> Mail            : 2106562095@qq.com
> Github          : www.github.com/Haroldcc
> Created Time    : 2020年02月26日  11时02分58秒
************************************************************************/

/***** 线性表的抽象定义 *****/

#ifndef LINEARLIST_H
#define LINEARLIST_H

#include <iostream>

template <typename T>
class linearList
{
public:
    virtual ~linearList() {}
    virtual bool isEmpty() const = 0;                         //是否为空
    virtual int size() const = 0;                             // 元素个数
    virtual T &getElement(int index) const = 0;               // 根据索引获取元素
    virtual void setElement(int index, const T &element) = 0; // 根据索引更改元素值
    virtual int indexOf(const T &element) const = 0;          //指定元素第一次出现的索引
    virtual void removeByIndex(int index) = 0;                //根据索引删除元素并返回
    virtual void removeByElement(const T &element) = 0;       // 删除指定元素
    virtual void add(const T &element) = 0;                   // 在尾部插入元素
    virtual void add(int index, const T &element) = 0;        // 在指定索引处插入元素
    virtual void output(std::ostream &out) const = 0;         // 插入输出流out
};

#endif

异常类(myException.h)

/*************************************************************************
> File Name       : myExceptions.h
> Author          : Harold
> Mail            : 2106562095@qq.com
> Github          : www.github.com/Haroldcc
> Created Time    : 2020年02月26日  14时32分01秒
************************************************************************/

/***** 自定义的异常 *****/
#ifndef MYEXCEPTIONS_H
#define MYEXCEPTUONS_H

#include <iostream>
#include <string>

// 非法参数
class illegalParameterValue
{
private:
    std::string message;

public:
    illegalParameterValue(std::string theMessage = "参数值非法!") : message(theMessage) {}
    void outputMessage() { std::cout << message << std::endl; }
};

// 非法输入
class illegalInputData
{
private:
    std::string message;

public:
    illegalInputData(std::string theMessage = "非法数据输入!")
    {
        message = theMessage;
    }

    void outputMessage() { std::cout << message << std::endl; }
};

// 非法索引
class illegalIndex
{
private:
    std::string message;

public:
    illegalIndex(std::string theMessage = "非法索引!") : message(theMessage) {}
    void outputMessage() { std::cout << message << std::endl; }
};
#endif

动态数组头文件(arrayList.h)

/*************************************************************************
> File Name       : arrayList.h
> Author          : Harold
> Mail            : 2106562095@qq.com
> Github          : www.github.com/Haroldcc
> Created Time    : 2020年02月26日  11时27分44秒
************************************************************************/
/***** 动态数组 *****/

#ifndef ARRAYLIST_H
#define ARRAYLIST_H

#include "linearList.h"
#include "myExceptions.h"
#include <iostream>
#include <sstream> // for std::ostringstream

template <typename T>
class arrayList : public linearList<T>
{
private:
    // 检测索引是否越界
    void checkIndex(int index) const;
    // 数组扩容
    void arrayListCapacityExpansion(T *&a, int oldCapacity, int newCapacity);
    // 数组缩容
    void arrayListCapacityReduce();

    T *m_elements;  // 数组元素
    int m_capacity; // 数组容量
    int m_size;     // 元素个数

public:
    // 构造函数,复制构造函数和析构函数
    arrayList(int initialCapacity = 10);
    arrayList(const arrayList<T> &);
    ~arrayList() { delete[] this->m_elements; }

    // ADT方法
    bool isEmpty() const { return this->m_size == 0; }
    int size() const { return this->m_size; }
    T &getElement(int index) const;
    void setElement(int index, const T &element);
    int indexOf(const T &element) const;
    void removeByIndex(int index);
    void removeByElement(const T &element);
    void add(const T &element);
    void add(int index, const T &element);
    void output(std::ostream &out) const;

    // 其他方法
    int capacity() const { return this->m_capacity; }
};

/*** 定义 ***/

template <typename T>
void arrayList<T>::checkIndex(int index) const
{
    // 确定index在0和m_size - 1之间
    if (index < 0 || index >= this->m_size)
    {
        std::ostringstream s;
        s << "index = " << index << " size = " << this->m_size;
        throw illegalIndex(s.str());
    }
}

template <typename T>
void arrayList<T>::arrayListCapacityExpansion(T *&a, int oldCapacity, int newCapacity)
{
    if (newCapacity < 0)
    {
        throw illegalParameterValue("新容量必须 >= 0");
    }

    T *temp = new T[newCapacity];                    // 新数组
    int number = std::min(oldCapacity, newCapacity); // copy的个数
    std::copy(a, a + number, temp);
    delete[] a; // 释放不在需要的就数组
    a = temp;   // 将新的数组赋给就数组指针
}

template <typename T>
void arrayList<T>::arrayListCapacityReduce()
{
    int oldCapacity = this->m_capacity;
    int newCapacity = oldCapacity >> 1; // 容量减少为原来的一半

    // 判断当size大于等于容量的一半或者容量小于等于默认容量,不需要缩容
    if (this->m_size >= newCapacity || oldCapacity <= 10)
    {
        return;
    }

    T *newElements = new T[newCapacity];
    for (int i = 0; i < this->m_size; i++)
    {
        newElements[i] = this->m_elements[i];
    }
    delete[] m_elements;

    this->m_elements = newElements;
    this->m_capacity = newCapacity;
}

template <typename T>
arrayList<T>::arrayList(int initialCapacity)
{
    if (initialCapacity < 1)
    {
        std::ostringstream s; // 构造新的字符串
        s << "初始容量 = " << initialCapacity << "必须 > 0!";
        throw illegalParameterValue(s.str());
    }
    this->m_capacity = initialCapacity;
    this->m_elements = new T[this->m_capacity];
    this->m_size = 0;
}

template <typename T>
arrayList<T>::arrayList(const arrayList<T> &list)
{
    this->m_capacity = list.m_capacity;
    this->m_size = list.m_size;
    this->m_elements = new T[this->m_capacity];

    // // 将数据拷贝过去(STL算法)
    // std::copy(list.element, list.element + this->m_size, this->element);

    for (int i = 0; i < this->m_size; i++)
        this->m_elements[i] = list.m_elements[i];
}

// 返回索引为index的元素,若不存在,抛出异常
template <typename T>
T &arrayList<T>::getElement(int index) const
{
    checkIndex(index);
    return this->m_elements[index];
}

// 设置指定索引处的元素值
template <typename T>
void arrayList<T>::setElement(int index, const T &element)
{
    checkIndex(index);
    this->m_elements[index] = element;
}

// 返回元素element第一次出现的索引,元素不存在,返回-1
template <typename T>
int arrayList<T>::indexOf(const T &element) const
{
    // // 使用STL算法
    // int elementIndex = (int)(std::find(this->element, this->element + this->m_size, element) - this->element);

    // if (elementIndex == this->m_size)
    // {
    //     // 未找到
    //     return -1;
    // }
    // else
    // {
    //     return elementIndex;
    // }

    // 遍历寻找元素
    for (int i = 0; i < this->m_size; i++)
        if (this->m_elements[i] == element)
            return i;

    return -1;
}

// 删除索引为 index 的元素,若元素不存在,抛出illegalIndex异常
template <typename T>
void arrayList<T>::removeByIndex(int index)
{
    checkIndex(index);

    // // STL算法
    // // 有效索引,移动其索引大于index的元素
    // std::copy(this->element + index + 1, this->element + this->m_size, this->element + index);

    for (int i = index + 1; i < this->m_size; i++)
    {
        this->m_elements[i - 1] = m_elements[i];
    }

    this->m_elements[--this->m_size].~T(); // 调用析构函数

    // 是否需要缩容
    arrayListCapacityReduce();
}

template <typename T>
void arrayList<T>::removeByElement(const T &element)
{
    removeByIndex(indexOf(element));
}

template <typename T>
void arrayList<T>::add(const T &element)
{
    add(this->m_size, element);
}

// 在索引 index 处插入元素 element
template <typename T>
void arrayList<T>::add(int index, const T &element)
{
    if (index < 0 || index > this->m_size) // 无效索引
    {
        std::ostringstream s;
        s << "index = " << index << " size = " << this->m_size;
        throw illegalIndex(s.str());
    }

    // 判断是否有足够容量’
    if (this->m_size == this->m_capacity)
    {
        int oldCapacity = this->m_capacity;
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        // 数组中元素个数等于容量,再添加需要扩容
        // 这里设置每次增加的容量是之前容量的1.5倍
        arrayListCapacityExpansion(this->m_elements, oldCapacity, newCapacity);
        this->m_capacity = newCapacity;
    }

    // 将数组元素向右迁移
    for (int i = this->m_size - 1; i >= index; i--)
    {
        this->m_elements[i + 1] = this->m_elements[i];
    }
    this->m_elements[index] = element;

    this->m_size++;
}

// 将arrayList放入输出流
template <typename T>
void arrayList<T>::output(std::ostream &out) const
{
    for (int i = 0; i < this->m_size; i++)
    {
        out << this->m_elements[i] << " ";
    }
}

// 重载 <<
template <typename T>
std::ostream &operator<<(std::ostream &out, const arrayList<T> &list)
{
    list.output(out);
    return out;
}
#endif

测试代码

#include "arrayList.h"
#include "linearList.h"
#include <iostream>

using namespace std;

int main()
{
     // 测试构造函数
     linearList<double> *x = new arrayList<double>(20);
     arrayList<int> y(2), z;

     // test capacity
     cout << "Capacity of x, y and z = "
          << ((arrayList<double> *)x)->capacity() << ", "
          << y.capacity() << ", "
          << z.capacity() << endl;

     // test size
     cout << "Initial size of x, y, and z = "
          << x->size() << ", "
          << y.size() << ", "
          << z.size() << endl;

     // test isEmpty
     if (x->isEmpty())
          cout << "x is isEmpty" << endl;
     else
          cout << "x is not isEmpty" << endl;
     if (y.isEmpty())
          cout << "y is isEmpty" << endl;
     else
          cout << "y is not isEmpty" << endl;

     // test add
     y.add(0, 2);
     y.add(1, 6);
     y.add(0, 1);
     y.add(2, 4);
     y.add(3, 5);
     y.add(2, 3);
     cout << "added 6 integers, list y should be 1 2 3 4 5 6" << endl;
     cout << "Size of y = " << y.size() << endl;
     cout << "Capacity of y = " << y.capacity() << endl;
     if (y.isEmpty())
          cout << "y is isEmpty" << endl;
     else
          cout << "y is not isEmpty" << endl;
     y.output(cout);
     cout << endl
          << "Testing overloaded <<" << endl;
     cout << y << endl;

     // test indexOf
     int index = y.indexOf(4);
     if (index < 0)
          cout << "4 not found" << endl;
     else
          cout << "The index of 4 is " << index << endl;

     index = y.indexOf(7);
     if (index < 0)
          cout << "7 not found" << endl;
     else
          cout << "The index of 7 is " << index << endl;

     // test getElement
     cout << "Element with index 0 is " << y.getElement(0) << endl;
     cout << "Element with index 3 is " << y.getElement(3) << endl;

     // test removeByIndex
     y.removeByIndex(1);
     cout << "Element 1 erased" << endl;
     cout << "The list is " << y << endl;
     y.removeByIndex(2);
     cout << "Element 2 erased" << endl;
     cout << "The list is " << y << endl;

     cout << "Size of y = " << y.size() << endl;
     cout << "Capacity of y = " << y.capacity() << endl;
     if (y.isEmpty())
          cout << "y is isEmpty" << endl;
     else
          cout << "y is not isEmpty" << endl;

     try
     {
          y.add(-3, 0);
     }
     catch (illegalIndex e)
     {
          cout << "Illegal index exception" << endl;
          cout << "add index must be between 0 and list size" << endl;
          e.outputMessage();
     }

     // test copy constructor
     arrayList<int> w(y);
     y.removeByIndex(0);
     y.removeByIndex(0);
     cout << "w should be old y, new y has first 2 elements removed" << endl;
     cout << "w is " << w << endl;
     cout << "y is " << y << endl;

     // a few more adds, just for fun
     y.add(0, 4);
     y.add(0, 5);
     y.add(0, 6);
     y.add(0, 7);
     cout << "y is " << y << endl;

     return 0;
}

输出

Capacity of x, y and z = 20, 2, 10
Initial size of x, y, and z = 0, 0, 0
x is isEmpty
y is isEmpty
added 6 integers, list y should be 1 2 3 4 5 6
Size of y = 6
Capacity of y = 6
y is not isEmpty
1 2 3 4 5 6
Testing overloaded <<
1 2 3 4 5 6
The index of 4 is 3
7 not found
Element with index 0 is 1
Element with index 3 is 4
Element 1 erased
The list is 1 3 4 5 6
Element 2 erased
The list is 1 3 5 6
Size of y = 4
Capacity of y = 6
y is not isEmpty
Illegal index exception
add index must be between 0 and list size
index = -3 size = 4
w should be old y, new y has first 2 elements removed
w is 1 3 5 6
y is 5 6
y is 7 6 5 4 5 6
posted @ 2020-02-27 14:59  HaroldC  阅读(323)  评论(0编辑  收藏  举报