C++STL | 容器元素淺拷貝深拷貝問(wèn)題及容器共性和使用場(chǎng)景

STL容器共性機(jī)制

STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是說(shuō)當(dāng)我們給容器中插入元素的時(shí)候,容器內(nèi)部實(shí)施了拷貝動(dòng)作,將我們要插入的元素再另行拷貝一份放入到容器中,而不是將原數(shù)據(jù)元素直接放進(jìn)容器中。
也就是說(shuō)我們提供的元素必須能夠被拷貝。

*除了queue和stack之外,每個(gè)容器都提供可返回迭代器的函數(shù),運(yùn)用返回的迭代器就可以訪問(wèn)元素。begin()和end()

*通常STL不會(huì)拋出異常,需要使用者傳入正確參數(shù)。

*每個(gè)容器都提供了一個(gè)默認(rèn)的構(gòu)造函數(shù)和默認(rèn)的拷貝構(gòu)造函數(shù)。

*大小相關(guān)的構(gòu)造方法: 1 size()返回容器中元素的個(gè)數(shù) 2 empty()判斷容器是否為空

void test_deep_copy(){
    char* data = "abcd";
    myclass mc(data); //創(chuàng)建myclass的實(shí)例 并用char*字符串data初始化對(duì)象

    vector<myclass> v;//創(chuàng)建vector容器
    v.push_back(mc); //將mc實(shí)例插入到vector容器尾部
}

我們?cè)诤瘮?shù)test_deep_copy()中創(chuàng)建了一個(gè)myclass對(duì)象,vector對(duì)象,這兩個(gè)對(duì)象在函數(shù)test_deep_copy()執(zhí)行完畢之后,會(huì)調(diào)用自身的析構(gòu)函數(shù),我們開(kāi)題說(shuō)了,STL容器都是值引用,再向容器中加入元素的時(shí)候,實(shí)際上是對(duì)元數(shù)據(jù)進(jìn)行了一份拷貝,將拷貝的數(shù)據(jù)放入到容器中,如下圖:


淺拷貝

由于我們沒(méi)有提供拷貝構(gòu)造函數(shù),沒(méi)有重載=操作符,vector對(duì)我們的mc對(duì)象進(jìn)行的簡(jiǎn)單的淺拷貝,將拷貝的對(duì)象插入到容器中,導(dǎo)致我們的mc對(duì)象的data指針和容器中mc對(duì)象的拷貝對(duì)象中的data指針都指向了我們?cè)诙褏^(qū)分配的內(nèi)存,當(dāng)函數(shù)結(jié)束,兩個(gè)對(duì)象都調(diào)用了析構(gòu)函數(shù),先調(diào)用析構(gòu)函數(shù)的對(duì)象成功釋放了堆區(qū)內(nèi)存,后調(diào)用析構(gòu)函數(shù)的對(duì)象一釋放,程序掛掉了。
原因在于兩個(gè)指針指向了同一塊堆區(qū)內(nèi)存,這樣會(huì)導(dǎo)致不可預(yù)知的結(jié)果,函數(shù)結(jié)束其中一個(gè)調(diào)用析構(gòu)函數(shù),銷毀了data指向的內(nèi)存空間,而另一個(gè)對(duì)象析構(gòu)的時(shí)候就會(huì)掛掉。

問(wèn)題的解決辦法就是,給我們的對(duì)象提供一個(gè)拷貝構(gòu)造函數(shù),并且重載=操作符,兩個(gè)指針?lè)謩e指向自己的那一塊內(nèi)存,互不影響。

class myclass{
public:
    myclass(char* data){
        int len = strlen(data) + 1; //計(jì)算傳進(jìn)來(lái)的字符串長(zhǎng)度
        this->data = new char[len];  //在堆區(qū)分配了len字節(jié)內(nèi)存
        strcpy(this->data, data); //將數(shù)據(jù)拷貝到我們?cè)诙逊峙涞膬?nèi)存中
    }
    //增加拷貝構(gòu)造函數(shù)
    myclass(const myclass& mc){
        int len = strlen(mc.data) + 1;
        this->data = new char[len];
        strcpy(this->data, mc.data);
    }
    //重載operator=操作符
    myclass& operator=(const myclass& mc){
        int len = strlen(mc.data) + 1;
        this->data = new char[len];
        strcpy(this->data, mc.data);
        return *this;
    }
    //既然我們?cè)诙褏^(qū)分配了內(nèi)存,需要在析構(gòu)函數(shù)中釋放內(nèi)存
    ~myclass(){
        if (NULL != this->data){
            delete[] this->data;
            this->data = NULL;
                 }
    }
private:
    char* data;
};

STL容器使用時(shí)機(jī)

STL容器使用時(shí)機(jī)

vector的使用場(chǎng)景:比如軟件歷史操作記錄的存儲(chǔ),我們經(jīng)常要查看歷史記錄,比如上一次的記錄,上上次的記錄,但卻不會(huì)去刪除記錄,因?yàn)橛涗浭鞘聦?shí)的描述。

deque的使用場(chǎng)景:比如排隊(duì)購(gòu)票系統(tǒng),對(duì)排隊(duì)者的存儲(chǔ)可以采用deque,支持頭端的快速移除,尾端的快速添加。如果采用vector,則頭端移除時(shí),會(huì)移動(dòng)大量的數(shù)據(jù),速度慢。

vector與deque的比較:
一:vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的開(kāi)始位置卻是不固定的。
二:如果有大量釋放操作的話,vector花的時(shí)間更少,這跟二者的內(nèi)部實(shí)現(xiàn)有關(guān)。
三:deque支持頭部的快速插入與快速移除,這是deque的優(yōu)點(diǎn)。

list的使用場(chǎng)景:比如公交車乘客的存儲(chǔ),隨時(shí)可能有乘客下車,支持頻繁的不確實(shí)位置元素的移除插入。

set的使用場(chǎng)景:比如對(duì)手機(jī)游戲的個(gè)人得分記錄的存儲(chǔ),存儲(chǔ)要求從高分到低分的順序排列。

map的使用場(chǎng)景:比如按ID號(hào)存儲(chǔ)十萬(wàn)個(gè)用戶,想要快速要通過(guò)ID查找對(duì)應(yīng)的用戶。二叉樹(shù)的查找效率,這時(shí)就體現(xiàn)出來(lái)了。如果是vector容器,最壞的情況下可能要遍歷完整個(gè)容器才能找到該用戶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容