欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++入門之vector的底層實現(xiàn)詳解

 更新時間:2021年11月18日 14:39:05   作者:捕獲一只小肚皮  
這篇文章主要為大家介紹了C++入門之vector的底層實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

前言

上一小節(jié),我們講解了vector的使用,也大概了解了其創(chuàng)建對象,增刪改查數(shù)據(jù)等操作.那么今天,我們就來大致實現(xiàn)一下吧.

定義初始結(jié)構(gòu)

在標準C++中,vector同樣是一個模板,并且其底層實現(xiàn)用的是三個指針,然后利用這三個指針相互加減,達到存儲效果.

而vector和string類似,本質(zhì)是都是一個順序表.

template <class T>
class vector
{
public:
    ~vector()
    {
        delete _start;
        _start = _finish = _end_of_storage = nullptr;
    }
private:
    T* _start;         //順序表的頭
    T* _finish;        //順序表有效長度位置
    T* _end_of_storage; //順序表末尾
};

聲明構(gòu)造函數(shù)

上一章節(jié)已經(jīng)講解,構(gòu)造函數(shù)比較多,這里只是為了簡單實現(xiàn),所以博主就實現(xiàn)一個最簡單的構(gòu)造函數(shù),即無參構(gòu)造.

vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr) {}

容量有關(guān)操作

獲取有效數(shù)據(jù)大小size()

想要獲取size,該怎么實現(xiàn)呢?我們在定義初始結(jié)構(gòu)的時候,已經(jīng)知道其底層是利用的三個指針,所以size等于_finish - _start.

size_t size() const    //加const是保證const對象也可以用
{
    return _finish - _start;
}

獲取數(shù)據(jù)容量capacity()

同樣的道理,capacity就應(yīng)是_end_of_storage - _start;

size_t capacity() //加const是保證const對象也可以用
{
    return _end_of_storage - _start;
}

增加容量reserve()

我們在后面會實現(xiàn)一個接口,叫做push_back(),它的作用是把數(shù)據(jù)放進vector,但如果空間不足了呢?這就需要我們的增容函數(shù)reserve了.

其參數(shù)是無符號整型n,代表要開n個空間

void reserve(size_t n)
{
    if (n > capacity())
    {
        T* tmp = new T[n];               //先開辟一塊空間
        size_t sz = size();              //保留原來的有效數(shù)據(jù)大小,且一定要先保存
        if (_start)        //一定要判斷這個,因為最開始_start為空,那么就只需要開空間
        {
            memcpy(tmp, _start, sizeof(T) * n);  //把原來的數(shù)據(jù)拷貝進新空間
            delete _start;  //釋放原來的空間
        }
        _start = tmp;                    //移交空間
        _finish = _start + sz;           //更新_finish
        _end_of_storage = _start + n;    //更新_end_of_storage
    }
}

重置大小resize()

這個想必大家現(xiàn)在已經(jīng)比較習(xí)慣了吧?他有兩個參數(shù),會分情況討論是否大于size()而進行參數(shù)賦值.

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;    
}

迭代器

前面講解string的時候說過,現(xiàn)階段我們就把迭代器當做一個指針,**雖然指針一定是迭代器,但是迭代器不一定是指針.**但是目前階段我們用到的迭代器就是一個指針,因此這里便直接定義迭代器了

typedef T* iterator;              //普通迭代器
typedef const T* const_iterator;  //const迭代器
//因此返回首尾迭代器的接口,博主便一并寫下來
iterator begin() {return _start;}
iterator end() {return _finish;}        //普通接口
const_iterator begin() const {return _start;}
const_iterator end() const {return _finish;}  //const接口

數(shù)據(jù)操作

尾插push_back()

該接口的實現(xiàn)操作一般是先檢查容量是否充足,然后把數(shù)據(jù)放進去,最后size大小加一.

void push_back(const T& val)
{
    if(size() == capacity())
    {
        size_t newcapacity = size()==0?4:capacity()*2;  //需要考慮到size是否有可能為0的情況
        reserve(newcapacity);
    }
    *_finish = val;
    _finish++;
}

尾刪pop_back()

實現(xiàn)該接口的操作一般是先檢查是否還存在數(shù)據(jù),然后size大小減一

void pop_back()
{
    assert(size()>0);
    _finish--;
}

某一位置插入 insert()

同樣的道理,一般先檢查容量是否充足,如果不夠,需要警惕迭代器失效問題,然后移動該位置及以后的所有數(shù)據(jù),最后插入數(shù)據(jù).

官方文檔定義其返回值為新插入數(shù)據(jù)的位置

iterator insert(iterator pos,const T& val)
{
    assert(pos>=_start && pos <= _finish);
    if(_finish == _end_of_storage)
    {
        int n = pos - _start;
        size_t newcapacity = 0 ? 4 :capacity()*2;
        pos = _start + n;      //防止pos迭代器失效
    }
    iterator cur = end();
    while(cur > pos)
    {
        *cur = *(cur-1);
        cur--;
    }
    *pos = val;
    _finish++;
    return pos;
}

某一位置刪除 erase()

該接口的操作一般是從pos后位置開始,所有數(shù)據(jù)前挪一單位.但是在挪之前,需要檢查是否還存在數(shù)據(jù)

官方文檔定義其返回值為刪除數(shù)據(jù)的下一位置

iterator erase(iterator pos)
{
    assert(pos >= _start && pos < _finish);
    iterator it = pos + 1;
    while (it != _finish)
    {
        *(it-1) = *it;
        ++it;
    }
    --_finish;
    return pos;
}

拷貝構(gòu)造

vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{
    reserve(v.capacity());
    for (const auto& e : v)
    {
        push_back(e);
    }
}

[]訪問操作

上面我們實現(xiàn)了迭代器,但是vector還支持[]索引取值,博主這里便實現(xiàn)兩個[]重載吧

T& operator[](size_t i)
{
    assert(i < size());
    return _start[i];
}
const T& operator[](size_t i) const
{
    assert(i < size());
    return _start[i];
}

=賦值操作

vector<T>& operator=(vector<T> v)    //注意哦~,我這里故意寫的傳值參數(shù),而不是引用,是為了下面進行交換
{
    swap(*this,v);
    return *this;
}

特別注意!!!

在實現(xiàn)了上面的一系列操作以后,有沒有覺得我們已經(jīng)大功告成了?其實這里還隱藏著一個小小bug!.為什么呢?看下面'

int main()
{
    //我們假設(shè)最開始創(chuàng)建的vector的容量是4
	vector<string> vc;
	vc.push_back("123");    //創(chuàng)建vc,并給其賦值
    vc.push_back("234");
    vc.push_back("345");
    vc.push_back("456");
    vc.push_back("567");
    return 0;
}

初一看,好像并沒有什么錯誤哎?但是一運行就會發(fā)現(xiàn),當插入第5個元素的時候,就報錯了.這是什么原因呢?

調(diào)試發(fā)現(xiàn),問題出在了reserve上面,因為push_back之前,會檢查容量,那么我們現(xiàn)在重新瞅瞅 reserve存在什么問題呢?

void reserve(size_t n)
{
    if (n > capacity())
    {
        T* tmp = new T[n];               //先開辟一塊空間
        size_t sz = size();              //保留原來的有效數(shù)據(jù)大小,且一定要先保存
        if (_start)        //一定要判斷這個,因為最開始_start為空,那么就只需要開空間
        {
            memcpy(tmp, _start, sizeof(T) * n);  //把原來的數(shù)據(jù)拷貝進新空間
            delete _start; //釋放原來的空間
        }
        _start = tmp;                    //移交空間
        _finish = _start + sz;           //更新_finish
        _end_of_storage = _start + n;    //更新_end_of_storage
    }
}

大家有發(fā)現(xiàn)什么問題了嗎?

沒錯,問題出現(xiàn)在memcpy上面,當容量不足,reserve就會增加容量,然后把原來空間的內(nèi)容值拷貝到新空間.

但是原來空間的內(nèi)容也就只有三個指針呀,拷貝過去后,新空間和源空間都指向了同一塊空間,而我們又會釋放原空間.

所以,當繼續(xù)尾插第5個元素時候,就報錯了,因為空間已經(jīng)不存在了!!!,被釋放了.

那怎么解決呢?這里便只能用循環(huán),把string值賦給新空間了.

void reserve(size_t n)
{
    if (n > capacity())
    {
        size_t sz = size();
        T* tmp = new T[n];
        if (_start)
        {
            for (size_t i = 0; i < sz; ++i)
            {	
                tmp[i] = _start[i];
            }
            delete[] _start;
        }
        _start = tmp;
        _finish = _start + sz;
        _endofstorage = _start + n;
    }

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • C++LeetCode數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解

    C++LeetCode數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解

    這篇文章主要介紹了C++實現(xiàn)LeetCode數(shù)據(jù)結(jié)構(gòu),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 關(guān)于C++中0是十進制還是八進制的問題

    關(guān)于C++中0是十進制還是八進制的問題

    本篇文章中,小編將為大家介紹關(guān)于C++中0是十進制還是八進制的問題,有需要的朋友可以參考一下
    2013-04-04
  • C++程序自動重啟的實現(xiàn)代碼

    C++程序自動重啟的實現(xiàn)代碼

    自動重啟原理很簡單,用一個進程監(jiān)控另一個進程,掛了就再啟動一個,細節(jié)也不算多,主要是正確判斷進程狀態(tài)和啟動方式,本文就給大家講講C++程序自動重啟的實現(xiàn)方法,文中有詳細的代碼示例供大家參考,需要的朋友可以參考下
    2024-04-04
  • 基于C++內(nèi)存分配、函數(shù)調(diào)用與返回值的深入分析

    基于C++內(nèi)存分配、函數(shù)調(diào)用與返回值的深入分析

    本篇文章是對C++中的內(nèi)存分配、函數(shù)調(diào)用與返回值進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • static關(guān)鍵字的作用詳解

    static關(guān)鍵字的作用詳解

    在C語言中,static的字面意思很容易把我們導(dǎo)入歧途,其實它的作用有三條。
    2013-04-04
  • 最新評論