一、 我們知道在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;
}
測試結果如下: