目錄
一、認識vector底層結構
二、初始化vector的函數
- 構造函數
- 拷貝構造
- 賦值構造
- initializer_list構造
- 迭代器區間構造
三、迭代器
四、數據的訪問
五、容量相關的函數
六、關于數據的增刪查改操作
一、認識vector底層結構
STL庫中實現vector其實是用三個指針來完成的,使用這三個指針(或稱為迭代器)變量來管理其內存
其實vector和string的實現非常相似,都是利用了順序表結構,在vector的實現上我們遵循底層用三個指針來完成,_statr,_finish,_end_fo_storage分別指向順序表的頭,順序表存儲數據的有效個數的位置,順序表的結束
template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start;iterator _finish;iterator _endofstorage;
};
二、初始化vector的函數
1、構造函數
①最常用的無參默認構造
vector();
vector()
:_strat(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
這個很簡單,我就不多講了,后面遇到難點我會詳細說明,便于大家理解
②用n個val構造一個vector
explicit vector (size_type n, const value_type& val = value_type();
庫里面用explicit修飾構造函數,是為了防止構造函數發生隱式類型轉換
vector(size_t n, const T& val = T())
{_start = new T[n];_finish = _start;while (_finish!=_start+n){*_finish = val;_finish++;}_endofstorage = _start + n;
}
2、拷貝構造
vector(const vector& v);
拷貝構造:用一個已經存在的對象來初始化另一個正在創建的對象
vector(const vector& v)
{_start = new T[v.size()];memcpy(_start, v._start, sizeof(T) * v.size());_finish = _start + v.size();_endofstorage = _finish;
}
3、賦值構造
賦值構造:兩個已經存在的對象,一個賦值給另一個
vector& operator= (const vector& v);
void swap(vector& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}
//v1=v2;
vector& operator= (vector v)
{swap(v);return *this;
}
這里我利用庫里的函數來實現swap,只交換vector的三個指針更高效,賦值構造的參數是傳值傳參,會調用拷貝構造,將v2拷貝一份給v,然后之間調用swap函數,將v和this交換,最后返回this即可
4、initializer_list構造
vector (initializer_list<T> il);
tips:這里的initializer_list實際是個類,C++底層將其封裝了,里面也有begin,end,size
//vector<int> v={1,2,3,4,5};
vector(initializer_list<T> il)
{for (auto e : il){push_back(e);}
}
5、迭代器區間構造
template <class InputIterator> vector(InputIterator first, InputIterator last);
// 類模板的成員函數可以是函數模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}
注意:如果加了迭代器區間構造會造成一個問題,就是在調用時和vector(size_t n, const T& val = T())
會出現沖突,底層給出的解決方案就是加一個重載vector(int n, const T& val = T())
三、迭代器
這里博主就只介紹最重要的迭代器部分template<class T>
class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const//加了const修飾,const對象可以調用,非const對象也可以調用{return _start;}const_iterator end() const{return _finish;}private:iterator _start; //指向空間(順序表)的開始iterator _finish;//指向有效數據個數的位置iterator _endofstorage;//指向空間的結束
};
四、數據的訪問
由于vector底層是連續的物理空間,所以支持下標訪問
T& operator[] (size_t n);
const T& operator[] (size_t n) const;
T& opTerator[](size_t n)
{assert(n < size());return _start[n];
}
const T& operator[](size_t n) const
{assert(n < size());return _start[n];
}
五、容量相關的函數
size(有效數據個數)和capacity(空間容量大小)
size_t size() const
{return _finish - _start;//指針減指針得到兩個指針之間的數據個數
}
size_t capacity() const
{return _endofstorage - _start;
}
reserve
void reserve (size_t n);
易錯點1
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* temp = new T[n];memcpy(temp, _start, sizeof(T) * size());delete[]_start;_start = temp;_finish = _start + old_size;_endofstorage = _start+n;}
}
改成現在這個樣子確實是解決了上圖中的問題,但是這個就是對的嗎?讓我們一起來看一下這個代碼究竟對不對
易錯點2
其實上面的代碼大體邏輯是沒有什么問題的,問題就出在這個memcpy上,要知道memcpy底層實現是按字節一個一個拷貝過去的,雖然我們的vector是深拷貝,但是memcpy是淺拷貝,這樣寫針對內置類型是🆗的,但是針對自定義類型會存在指向同一塊空間的問題,兩次析構等問題,話不多說,我們看圖解
出錯案例:
出錯圖解:
其實要改正這個問題有一個很簡單的方式,采用賦值的方式,無論是內置類型還是自定義類型,在賦值時都會調用他的拷貝構造,這樣就自動調用該類型的深拷貝了
void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* temp = new T[n];//memcpy(temp, _start, sizeof(T) * size());for (size_t i = 0; i < old_size; i++){temp[i] = _start[i];}delete[]_start;_start = temp;_finish = _start + old_size;_endofstorage = _start+n;}
}
resize
void resize (size_t n, const T& val=T());
void resize(size_t n, const T& val=T())
{if (n > capacity()){reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}else{_finish = _start + n;}
}
注意:reverse和resize都不會縮容
empty
bool empty() const
bool empty()const
{return _finsh == _start;
}
六、關于數據的增刪查改操作
push_back
void push_back (const T& val);
void push_back(const T& val)
{if (size() == capacity()){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = val;_finish++;
}
inserrt
void insert (iterator pos, const T& val);
void insert(iterator pos, const T& val)
{assert(pos>=_start);assert(pos <= _finish);size_t d = pos - _start;//先記下pos和_start的相對位置if (size() == capacity()){reserve(capacity() == 0 ? 4 : 2 * capacity());//如果擴容了,要更新pospos = _start + d;}iterator end = _finish;while (pos < end){*end = *(end - 1);end--;}*pos = val;_finish++;
}
erase
iterator erase (iterator pos);
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator cur = pos;while (cur +1< _finish){*(cur) = *(cur + 1);cur++;}_finish--;return pos;
}
clear
void clear();
void clear()
{_finish = _start;
}
vector章節我們就到這里啦,歡迎大家來學習指教下一篇list章節😘