1. 程式人生 > 其它 >C++實現簡單版本的vector/stack/queue

C++實現簡單版本的vector/stack/queue

一、vector

只實現標準的順序表vector容器,不涉及演算法和迭代器。

核心思想就是通過new操作得到的動態陣列。

new的是容量大小的空間,_size是操作的數量。

類定義如下。

#include <iostream>
using namespace std;
#define DEFAULT_CAPACITY 10 //預設容量
template<typename _Tp>
class Vector
{
private:
    //定義一些基本的成員變數
    _Tp* _data = nullptr;//指向_Tp型別元素的指標,旨在藉助動態陣列實現Vector
    size_t _size = 0
;//儲存元素的個數 size_t _capacity = DEFAULT_CAPACITY;//容量,即當前動態陣列的大小 public: //構造:普通構造、拷貝構造、移動構造 //賦值:拷貝賦值、移動賦值 //陣列操作:尾部增加,刪除某索引,獲取某索引,獲取元素個數 explicit Vector(size_t capacity = DEFAULT_CAPACITY); //省略下面兩個構造和兩個賦值的實現 //Vector(const Vector<_Tp>& other); //Vector(Vector<_Tp>&& other);
//Vector(const Vector<_Tp>& other); //Vector(Vector<_Tp>&& other); void push_back(const _Tp& element); void remove_index(size_t index); _Tp& at(size_t index); const _Tp& at(size_t index) const;//上者的const過載 size_t size() const; ~Vector(); private
: //這是最核心的操作,根據當前元素的數量,對陣列進行擴充套件 void extend(); };

具體函式實現如下。

template<typename _Tp>
void Vector<_Tp>::extend()//這是vector底層實現最關鍵的空間分配操作
{
    //沒有滿就不用擴充套件
    if (_size < _capacity) return;
    //否則就要進行兩倍擴容,暫存以前的資料。然後獲取兩倍空間,然後拷貝過去。
    _capacity <<= 1;
    _Tp* oldData = _data;
    _data = new _Tp[_capacity];
    for (int i = 0; i < _size; i++) _data[i] = oldData[i];
    delete[] oldData;//釋放原本的記憶體空間
}

template<typename _Tp>
Vector<_Tp>::Vector(size_t capacity)
{
    _capacity = capacity;
    _data = new _Tp[capacity];
}

template<typename _Tp>
Vector<_Tp>::~Vector()
{
    //清除動態陣列的所有元素
    if (_data)
    {
        delete[] _data;
    }
}

template<typename _Tp>
void Vector<_Tp>::push_back(const _Tp& element)
{
    //首先檢查是否需要擴充套件
    extend();
    _data[_size] = element;//直接載入_data的尾部
    ++_size;//更新_size
}

template<typename _Tp>
void Vector<_Tp>::remove_index(size_t index)
{
    //把index後面的元素往前移動
    for (int i = index; i < _size + 1; ++i)
    {
        _data[i] = _data[i + 1];
    }
    --_size;
}

template<typename _Tp>
_Tp& Vector<_Tp>::at(size_t index)
{
    return _data[index];
}

template<typename _Tp>
const _Tp& Vector<_Tp>::at(size_t index) const
{
    return _data[index];
}

template<typename _Tp>
size_t Vector<_Tp>::size() const
{
    return _size;
}

測試如下。

int main()
{
    Vector<int> vec;
    for (int i = 0; i < 21; ++i)
    {
        vec.push_back(i);
        cout << vec.at(i) << endl;
    }
    vec.remove_index(17);
    for (int i = 0; i < vec.size(); i++)
    {
        cout << vec.at(i) << ", ";
    }
    cout << endl;
    return 0;
}

 

二、stack

棧的主要特點是先進後出。

主要內容就是定義了一組操作,和底層資料結構關聯不大。因此使用上述實現的Vector。

主要操作:入棧、出棧、返回棧頂、是否為空。

類定義和具體實現如下。

#include <iostream>
#include "my_vector.h"
using namespace std;

template<typename _Tp>
class Stack
{
private:
    Vector<_Tp> _data;//vector的尾部就是stack的頂部
    /*
   0  ______________  size-1 (棧頂)
     |______________
    */
public:
    void push(const _Tp& val) 
    {
        _data.push_back(val);
    }
    _Tp pop() //先暫存再刪除,最後返回暫存
    {
        _Tp val = _data.at(_data.size() - 1);
        _data.remove_index(_data.size() - 1);
        return val;
    }
    const _Tp& top() const
    {
        return _data.at(_data.size() - 1);
    }
    bool empty() const
    {
        return _data.size() == 0;
    }
};

測試如下。

int main()
{
    Stack<int> st;
    st.push(1);
    cout << "top:" << st.top() << endl;
    return 0;
}

 

三、queue

先進先出,主要內容就是定義了一組操作,和底層資料結構關聯不大。

這裡不使用上述實現的Vector,因為queue是隊尾進,隊頭出。

如果使用前面實現的Vector,入隊還好,出隊會造成大量的元素移動操作。

兩個指標分別是begin和end。

採用迴圈利用的方式,出隊之後的元素並不移動,空出的位置就作為接下來入隊元素的備用空間。

迴圈利用的核心操作就是取餘運算,既能省時間又避免了出隊後空出的空間不再被利用造成的空間的浪費。

核心操作動態擴容的策略和前面的一樣,都是擴充兩倍。

主要操作:入隊、出隊、返回隊頂、是否為空。

類定義如下。

    /*
 0(隊頭)     ______________  size-1 (隊尾)
            ______________
    */
#include <iostream>
using namespace std;
//#define DEFAULT_CAPACITY2 10 //預設容量

template<typename _Tp>
class Queue1
{
private:
    _Tp* _data = nullptr;//動態陣列指標
    int _begin = 0;
    int _end = 0;
    //size_t _capacity = (size_t)DEFAULT_CAPACITY2;
    size_t _capacity = (size_t)10;
public:
    Queue1();
    ~Queue1();
    void push(const _Tp& element);
    void pop();
    const _Tp& front() const;
    size_t size() const;
    bool empty() const;
private:
    //這是最核心的操作,根據當前元素的數量,對陣列進行擴充套件
    void extend();
};

具體函式實現如下。

template<typename _Tp>
Queue1<_Tp>::Queue1()
{
    _data = new _Tp[_capacity];
}

template<typename _Tp>
Queue1<_Tp>::~Queue1()
{
    if (_data) delete[] _data;
}

template<typename _Tp>
size_t Queue1<_Tp>::size() const
{
    return (_end - _begin + _capacity) % _capacity;
}

template<typename _Tp>
bool Queue1<_Tp>::empty() const
{
    return _begin == _end;
}

template<typename _Tp>
const _Tp& Queue1<_Tp>::front() const
{
    return _data[_begin];
}

template<typename _Tp>
void Queue1<_Tp>::push(const _Tp& element)
{
    extend();
    _data[_end] = element;
    _end = (_end + 1) % _capacity;
}

template<typename _Tp>
void Queue1<_Tp>::pop()
{
    if (!empty())//不空才出佇列
    {
        _data[_begin].~_Tp();
        _begin = (_begin + 1) % _capacity;//迴圈後移
    }
}

template<typename _Tp>
void Queue1<_Tp>::extend()
{
    size_t size_one = size();
    if (size_one == _capacity - 1)
    {
        _Tp* tmp = new _Tp[_capacity << 1];
        for (int i = _begin, j = 0; i != _end; i = (i + 1) % _capacity, ++i)
        {
            tmp[j] = _data[i];
        }
        _begin = 0;
        _end = size_one;
        delete[] _data;
        _data = tmp;
        _capacity <<= 1;
    }
}

測試如下。

int main()
{
    Queue1<int> que;
    que.push(1);
    cout << "front:" << que.front() << endl;
    cout << "size:" << que.size() << endl;
    return 0;
}