實現C++標準庫STL小型向量vector

一、 我們知道在C++標準庫中有一類很重要的容器:向量vector,其實STL底層實現vector比較複雜,這裏我們使用簡單的語法實現自己的vector,功能沒有標準庫的豐富,但包含一些常用的接口,讀者也可以自己添加接口和工具函數等。實現過程如下(代碼可復現

1)在STL中,所有的容器都是類模板,這裏我們也使用類模板,首先看一下定義的外部接口(成員函數):

在這裏插入圖片描述

2)定義的私有成員:

在這裏插入圖片描述
在這裏插入圖片描述

3)此外:我們還定義一些工具函數(被拷貝構造函數、賦值運算符和析構函數使用)

在這裏插入圖片描述

二、各部分定義:

1、來看一下vector的構造函數:

首先是簡單的初始化:將三個指針都初始化爲空指針(nullptr),作用相當於合成默認構造器

在這裏插入圖片描述

第二個是使用一個initializer_list 類型的參數(可變參數),構造MyTinyVec,類似於標準庫中的列表初始化(包括直接初始化和拷貝初始化)。

在這裏插入圖片描述

2、定義拷貝成員(拷貝構造函數、賦值運算符和析構函數)

在這裏插入圖片描述
在這裏插入圖片描述

3、其他成員的定義(一些簡單的成員即定義爲內聯函數)

3.1獲取元素的數量和容器的容量:
在這裏插入圖片描述
3.2 獲取首元素和尾後元素的指針:
在這裏插入圖片描述
3.3 重載下標運算符(有const 和非const 的兩個版本,重載下標運算符一般都要定義兩個):
在這裏插入圖片描述
3.4 在尾部空的位置插入元素:
在這裏插入圖片描述
我們可以看到,我們在其中的一些成員函數中使用了一些工具函數,例如free(),check_n_copy()等等,其實我們定義的這些工具纔是核心(下附代碼

我們在代碼中看整個MyTinyVec類的實現(以下爲實現部分,代碼可復現):

MyTinyVec.hpp  中文件聲明和定義
#pragma once
#include <iostream>
#include <memory>
#include <initializer_list>
using namespace std;

template <class T>
class MyTinyVec
{
public:
	MyTinyVec();                           //默認的構造和自定義構造
	MyTinyVec(initializer_list<T> il);
	MyTinyVec(const MyTinyVec &);          //拷貝構造
	MyTinyVec & operator=(MyTinyVec &);    //拷貝賦值
	T& operator[](size_t index);           //重載下標運算符
	const T& operator[](size_t index) const;
	std::size_t size() const;              //獲得容器的大小和容量
	std::size_t capacity() const;
	void resreve(unsigned num);            //預留空間
	T * begin();                           //首元素的地址
	T * end();                             //尾元素地址
	void push_back(const T &);
	virtual~MyTinyVec();

private:
	void check_n_alloc();           //檢查是否有空間添加新的元素,否則會調用reallocate函數
	std::pair<T *, T *> alloc_n_copy(const T *b,const T *e);    //分配內存,拷貝一個給定範圍的元素
	void reallocate();              //重新分配空間
	void free();                    //銷燬構造的元素並釋放內存
	std::allocator<T> alloc; 
	T *elements;        //首元素指針
	T *first_free;      //最後一個元素的下一位置
	T *cap;             //內存空間的下一位置
};

//構造函數

template <class T>
MyTinyVec<T>::MyTinyVec():elements(nullptr),first_free(nullptr),cap(nullptr) {
}

//自定義構造函數

template<class T>
inline MyTinyVec<T>::MyTinyVec(initializer_list<T> il) {
	pair<T *, T *> newdata = alloc_n_copy(il.begin(), il.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

//拷貝構造

template<class T>
inline MyTinyVec<T>::MyTinyVec(const MyTinyVec &mv){
	//此時data是一個pair型;first 和 second
	std::pair<T *, T*> data = alloc_n_copy(mv.begin(), mv.end());
	elements = data.first;
	first_free = cap = data.second;
}

//拷貝賦值運算符

template<class T>
inline MyTinyVec<T> & MyTinyVec<T>::operator=(MyTinyVec<T> & mv){
	pair<T *,T *> data = alloc_n_copy(mv.begin(),mv.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}

//重載下標運算符

template<class T>
inline T & MyTinyVec<T>::operator[](size_t index){
	return elements[index];
}

template<class T>
inline const T & MyTinyVec<T>::operator[](size_t index) const
{
	return elements[index];
}

//獲取大小

template <class T>
inline std::size_t MyTinyVec<T>::size() const {
	return (first_free - elements);
}

//獲取容量

template <class T>
inline std::size_t MyTinyVec<T>::capacity() const {
	return (cap - elements);
}

//預分配空間

template<class T>
inline void MyTinyVec<T>::resreve(unsigned num){
	//auto dataPtr = alloc_n_copy(alloc.begin(),alloc.end());
	//auto dataPtr = alloc.allocate(num);
	cap = elements + num;
}

//獲取首元素地址

template<class T>
inline T * MyTinyVec<T>::begin(){
	return elements;
}

//獲取尾後元素地址

template<class T>
inline T * MyTinyVec<T>::end(){
	return cap ;
}

//添加元素

template<class T>
void MyTinyVec<T>::push_back(const T &e){
	check_n_alloc();
	alloc.construct(first_free++, e);
}

//析構函數

template <class T>
MyTinyVec<T>::~MyTinyVec() {
	free();
}

//工具函數
//檢查是否有空間插入元素

template <class T>
inline void MyTinyVec<T>::check_n_alloc() {
	if (size() == capacity())
		reallocate();
}

//舊空間拷貝至新的內存空間

template<class T>
std::pair<T *, T *> MyTinyVec<T>::alloc_n_copy(const T *b,const T *e){
	T* data = alloc.allocate(e - b);   
	return {data, std::uninitialized_copy(b,e,data)};
}

//重新分配內存空間
//分爲三步:1、移動原來的元素進新空間2、釋放原空間3、更新指針

template<class T>
inline void MyTinyVec<T>::reallocate(){
	std::size_t newCapacity = this->capacity() * 2;
	auto newData = alloc.allocate(newCapacity);
	auto dest = newData;
	auto elem = elements;
	for (std::size_t i = 0; i < size(); ++i) {
		alloc.construct(dest++,std::move(*elem++));
	}
	free();
	elements = newData;
	first_free = dest;
	cap = elements + newCapacity;
}

//free釋放函數

template<class T>
inline void MyTinyVec<T>::free(){
	if (elements) {
		for (T *p = elements; p != first_free;) {
			alloc.destroy(p++);
		}
		alloc.deallocate(elements, cap - elements);
	}
}

最後是測試部分(main函數):

#include "myTinyVec.hpp"

int main() {
	cout << "**************************" << endl;
	cout << "未初始化的string類型的vector,設置預留空間爲10:" << endl;
	MyTinyVec<string> svec;
	svec.resreve(10);
	cout << "元素數有:" << svec.size() << endl;
	cout << "容器的容量:" <<svec.capacity() << endl;
	cout << "**************************" << endl;
	cout << "使用列表初始化{1,2,3,4,5}後的int類型vector" << endl;
	MyTinyVec<int> ivec({1,2,3,4,5});
	cout << "元素數有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	cout << "**************************" << endl;
	cout << "又使用成員push_back插入一個元素後:" << endl;
	ivec.push_back(6);
	cout << "元素數有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	cout << "**************************" << endl;
	cout << "改變容量爲20後:" << endl;
	ivec.resreve(20);
	cout << "元素數有:" << ivec.size() << endl;
	cout << "容器的容量:" << ivec.capacity() << endl;
	for (size_t i = 0; i != ivec.size(); ++i) {
		cout << ivec[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

測試結果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章