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

C++為什么不能修改set里的值?非要修改怎么辦?

 更新時(shí)間:2021年11月03日 16:44:52   作者:Coder_LT  
因?yàn)橹暗奈恼掠姓f(shuō)過(guò)C++中 set的介紹及用法,今天這篇文章我們就來(lái)說(shuō)說(shuō)C++為什么不能修改set里的值,如果非要修改的話(huà)應(yīng)該怎么辦,下面我們一起進(jìn)入文章看看下面內(nèi)容,需要的朋友可以參考以下,希望對(duì)你有所幫助

在上一期C++中 set的用法文章當(dāng)中講解了set的一些常規(guī)用法和api,最后末尾的時(shí)候留了一個(gè)問(wèn)題,如何修改set中的元素?今天就來(lái)聊聊這個(gè)問(wèn)題。

很多同學(xué)估計(jì)會(huì)說(shuō),這還不簡(jiǎn)單,不是有迭代器么。我們把迭代器當(dāng)做指針,去修改它指向的值不就行了嗎?

like this:

set<string> st{"hello", "world", "good"};
set<string>::iterator it = st.begin();
*it = "test";

但是很遺憾,你真這么做了,會(huì)得到一個(gè)報(bào)錯(cuò):

報(bào)錯(cuò)的意思是set的迭代器并沒(méi)有重載等于符號(hào),也就是說(shuō)我們沒(méi)辦法使用等于符號(hào)來(lái)為它賦值。說(shuō)白了,也就是編譯器進(jìn)行了限制,不允許我們對(duì)set迭代器的內(nèi)容進(jìn)行修改。

Effective C++當(dāng)中也明確說(shuō)了,不要對(duì)set集合中的元素進(jìn)行修改。

不知道有沒(méi)有小伙伴去嘗試,可能有些小伙伴嘗試了之后會(huì)說(shuō)不對(duì)啊,在我電腦上怎么能運(yùn)行?

也很簡(jiǎn)單,大概率因?yàn)槟阌玫氖莢c編譯器,比如臭名昭著的VC6.0或者是visual studio IDE(不是VSCode)。微軟的編譯器沒(méi)有嚴(yán)格遵循C++的標(biāo)準(zhǔn),在很多地方有些瑕疵和隨意。這也是不推薦使用VC6.0進(jìn)行C++學(xué)習(xí)的原因,因?yàn)闀r(shí)間久了,就把錯(cuò)的當(dāng)成對(duì)的了。

吐槽完畢,回到正題。既然已經(jīng)知道了這樣修改會(huì)引發(fā)報(bào)錯(cuò),是不是就已經(jīng)得到了答案了呢?

其實(shí)并沒(méi)有,因?yàn)槿绻覀冋娴娜ラ喿xC++的標(biāo)準(zhǔn)或者是翻閱set的源碼,會(huì)發(fā)現(xiàn)其中是沒(méi)有明確說(shuō)明set中的元素是定義成const的。

實(shí)際上,std::set<T>聲明一個(gè)allocator_type,默認(rèn)為std::allocator<T>。std::allocator_traits<allocator_type>::construct將它傳遞給T *,從而構(gòu)造一個(gè)T,而不是const T。

說(shuō)人話(huà)就是std::set<T>其實(shí)不允許將元素定義成const,既然元素不是const類(lèi)型,那么就說(shuō)明理論上是可以修改的。也就是說(shuō)C++規(guī)范里說(shuō)不能改,Effective C++中說(shuō)建議不要改,但實(shí)際上底層的實(shí)現(xiàn)里并沒(méi)有嚴(yán)格禁止。我們非要改還是有辦法的,那是什么辦法呢?

老梁縱觀全網(wǎng)博客,也沒(méi)有看到一篇把這個(gè)問(wèn)題說(shuō)清楚。

在我們開(kāi)始之前,首先思考一個(gè)問(wèn)題,既然set底層源碼當(dāng)中的元素并不是定義成const,那么當(dāng)我們?nèi)ビ玫魅バ薷牡臅r(shí)候?yàn)槭裁磿?huì)報(bào)錯(cuò)呢?

要回答這個(gè)問(wèn)題,我們只需要查看一下set迭代器的源碼定義即可。

老梁在大牛的源碼分析當(dāng)中找到了一行關(guān)鍵的代碼:

原來(lái)迭代器的定義是一個(gè)const_iterator,搞了半天,其實(shí)并不是set底層限制了禁止修改,而是通過(guò)迭代器限制的。所以要想修改set當(dāng)中的元素,我們只需要繞開(kāi)迭代器的這個(gè)限制即可。

進(jìn)一步研究可以發(fā)現(xiàn),它這里使用的是一個(gè)const_iterator,它表示一個(gè)指向常量的迭代器,和const iterator不同。后者表示迭代器本身是一個(gè)常量,即迭代器本身指向的位置不能修改。而前者表示迭代器指向的位置是一個(gè)const常量,迭代器本身可以修改,指向不同的位置,但我們不能修改它指向的位置的值。

const_iterator并沒(méi)有嚴(yán)格限制只能指向const修飾的變量,這也就能解釋為什么set當(dāng)中的元素沒(méi)有const修飾也不會(huì)報(bào)錯(cuò)的原因,因?yàn)?code>const_iterator兼容這種情況。

const_iterator解引用之后是一個(gè)const修飾的變量的引用,所以我們要對(duì)它指向的內(nèi)容進(jìn)行修改,只需要將它解引用的結(jié)果去除const限制即可。那具體怎么操作呢,我們可以使用const_cast操作符解除const的限制。

但它也不是萬(wàn)能的,它只能使用在引用和指針當(dāng)中,用來(lái)去掉const屬性。

這里有必要說(shuō)明一下,在C++當(dāng)中const修飾符出現(xiàn)的位置不同有不同的含義。以指針舉例,const T* pT* const p是兩種完全不同的指針,前者表示不能通過(guò)指針去修改指向?qū)ο蟮膬?nèi)容。如p->x = 100;這樣的操作都是非法的。而后者表示指針只能在初始化時(shí)設(shè)置指向的內(nèi)容,之后不能修改指向,如p=&t;是非法的。

在當(dāng)前問(wèn)題當(dāng)中,我們想要修改set當(dāng)中的元素值,遇到了const限制,顯然是第一種情況。

有些同學(xué)可能會(huì)覺(jué)得疑惑,我們加上const的目的不就是為了對(duì)變量做限制,從而可以在編譯的時(shí)候通過(guò)編譯器來(lái)替我們檢查一些非法的操作嗎?既然如此,又為什么需要去掉呢?

主要的原因是有時(shí)候我們手上的變量有const修飾,但是我們想要調(diào)用一個(gè)函數(shù),而函數(shù)的內(nèi)部會(huì)對(duì)指針或引用指向的值進(jìn)行修改。這個(gè)時(shí)候我們就沒(méi)辦法傳入我們手上已有的參數(shù)了,const_cast操作符設(shè)計(jì)的初衷就是為了應(yīng)對(duì)這種情況。

我們來(lái)舉個(gè)例子:

void test(int *x) {
 *x = 5;
}

int main() {
 int a = 3;
 const int *p = &a;

 test(p);
 return 0;
}

如果我們編譯上面這段代碼就會(huì)遇到編譯器無(wú)情地報(bào)錯(cuò),因?yàn)槲覀冊(cè)趖est函數(shù)內(nèi)部修改了指針p的指向。

這個(gè)時(shí)候我們就可以在傳參的時(shí)候,使用const_cast操作符來(lái)解除掉const的限制。

test(const_cast<int*>(p));

尖括號(hào)中是我們要轉(zhuǎn)換的類(lèi)型,只能是指針或引用。如果我們輸出指針p指向的值,會(huì)得到5,因?yàn)樵趖est函數(shù)當(dāng)中進(jìn)行了修改。

看起來(lái)好像很簡(jiǎn)單,對(duì)吧?

但是我們接下來(lái)看兩個(gè)例子,可能會(huì)令人有些費(fèi)解:

const int a = 3;
int *r = const_cast<int*>(&a);
(*r)++;
cout << a << endl;

int i = 3;
const int b = i;
int *r2 = const_cast<int*>(&b);
(*r2)++;
cout << b << endl;

這兩段代碼做的事情非常類(lèi)似,也就是通過(guò)const_cast修改了一個(gè)const修飾的int。唯一的不同是int a是直接賦值成了3,而int b是賦值成了另外一個(gè)也等于3的int。這兩者其實(shí)并沒(méi)有什么區(qū)別,對(duì)吧?但是當(dāng)我們運(yùn)行代碼之后,神奇的事情發(fā)生了,

屏幕上輸出的結(jié)果是這樣的:


為什么一個(gè)是3,另外一個(gè)是4呢?這兩者的邏輯明明是一樣的!

老梁發(fā)現(xiàn)這個(gè)問(wèn)題的時(shí)候是完全震驚的,查了好久的資料,才從大牛博客的只言片語(yǔ)當(dāng)中找到了一點(diǎn)描述。原來(lái)是編譯器針對(duì)第一種情況做了優(yōu)化,因?yàn)閍初始化時(shí)給的是一個(gè)常量,所以當(dāng)我們輸出的時(shí)候,編譯器就直接取了3代替了它實(shí)際原本應(yīng)該的值。

關(guān)于這個(gè)解釋老梁也不能完全確認(rèn),如果有知道的小伙伴不妨在下方留言。

最后, 我們回到正題,如果我們想要修改set當(dāng)中的元素,可以怎么操作呢?

我們知道了const_cast操作符之后就完全沒(méi)有懸念了。

set<string> st{"hello", "world", "good"};
set<string>::iterator it = st.begin();
const_cast<string&>(*it) = "test";

for (auto it = st.begin(); it != st.end(); it++) {
    cout << *it << endl;
}

但是我們需要注意一點(diǎn),我們這樣強(qiáng)行修改其實(shí)是沒(méi)有經(jīng)過(guò)set原本的機(jī)制的。也就是說(shuō)我們雖然改了元素的值,但是它在紅黑樹(shù)中的位置其實(shí)是沒(méi)有變的。這樣的結(jié)果就是會(huì)導(dǎo)致元素失去有序性,比如上面的結(jié)果輸出的順序是:"test","hello","world",按道理應(yīng)該是按照字典順序排序的。

這也是為什么C++ Primer里強(qiáng)烈建議大家不要修改set中元素值的原因,如果真的要修改,只能先刪除再添加了。雖然這樣會(huì)犧牲一點(diǎn)點(diǎn)性能,但至少可以保證set里的數(shù)據(jù)都是安全有序的。

到此這篇關(guān)于C++為什么不能修改set里的值?非要修改怎么辦?的文章就介紹到這了,更多相關(guān)C++修改set里的值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

注:文章轉(zhuǎn)自微信眾號(hào):Coder梁(ID:Coder_LT)

相關(guān)文章

  • 詳解C++的JSON靜態(tài)鏈接庫(kù)JsonCpp的使用方法

    詳解C++的JSON靜態(tài)鏈接庫(kù)JsonCpp的使用方法

    這篇文章主要介紹了C++的JSON靜態(tài)鏈接庫(kù)JsonCpp的使用方法,演示了使用JsonCpp生成和解析JSON的方法,以及C++通過(guò)JSON方式的socket通信示例,需要的朋友可以參考下
    2016-03-03
  • pthread_cond_wait() 用法深入分析

    pthread_cond_wait() 用法深入分析

    以下是對(duì)pthread_cond_wait的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下
    2013-07-07
  • C語(yǔ)言 從根本上理解數(shù)組

    C語(yǔ)言 從根本上理解數(shù)組

    數(shù)組是一組有序的數(shù)據(jù)的集合,數(shù)組中元素類(lèi)型相同,由數(shù)組名和下標(biāo)唯一地確定,數(shù)組中數(shù)據(jù)不僅數(shù)據(jù)類(lèi)型相同,而且在計(jì)算機(jī)內(nèi)存里連續(xù)存放,地址編號(hào)最低的存儲(chǔ)單元存放數(shù)組的起始元素,地址編號(hào)最高的存儲(chǔ)單元存放數(shù)組的最后一個(gè)元素
    2022-04-04
  • C++面向?qū)ο缶幊讨鰳?gòu)詳解

    C++面向?qū)ο缶幊讨鰳?gòu)詳解

    這篇文章主要為大家詳細(xì)介紹了C++面向?qū)ο缶幊讨鰳?gòu),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • C語(yǔ)言函數(shù)指針詳解

    C語(yǔ)言函數(shù)指針詳解

    本文主要介紹 C語(yǔ)言函數(shù)指針的知識(shí),這里整理了詳細(xì)的資料及示例代碼以便大家學(xué)習(xí)參考,有需要學(xué)習(xí)此部分知識(shí)的朋友可以參考下
    2021-09-09
  • 淺談C++類(lèi)型轉(zhuǎn)化(運(yùn)算符重載函數(shù))和基本運(yùn)算符重載(自增自減)

    淺談C++類(lèi)型轉(zhuǎn)化(運(yùn)算符重載函數(shù))和基本運(yùn)算符重載(自增自減)

    下面小編就為大家?guī)?lái)一篇淺談C++類(lèi)型轉(zhuǎn)化(運(yùn)算符重載函數(shù))和基本運(yùn)算符重載(自增自減)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • 關(guān)于在MFC中將窗口最小化到托盤(pán)實(shí)現(xiàn)原理及操作步驟

    關(guān)于在MFC中將窗口最小化到托盤(pán)實(shí)現(xiàn)原理及操作步驟

    最小化的原理:首先要將窗口隱藏,然后在右下角繪制圖標(biāo);恢復(fù)的原理:將窗口顯示,再將托盤(pán)中的圖片刪除,接下來(lái)介紹實(shí)現(xiàn)方法,感興趣的朋友可以了解下啊,希望本文對(duì)你有所幫助
    2013-01-01
  • 解析C語(yǔ)言基于UDP協(xié)議進(jìn)行Socket編程的要點(diǎn)

    解析C語(yǔ)言基于UDP協(xié)議進(jìn)行Socket編程的要點(diǎn)

    這篇文章主要介紹了C語(yǔ)言通過(guò)UDP協(xié)議進(jìn)行Socket編程的要點(diǎn),文中還提到了相關(guān)ARP與ICMP協(xié)議的作用,需要的朋友可以參考下
    2016-02-02
  • C++通信新特性協(xié)程詳細(xì)介紹

    C++通信新特性協(xié)程詳細(xì)介紹

    這篇文章主要給大家分享得是C++的特性協(xié)程Coroutine,下面文章內(nèi)容我們將來(lái)具體介紹什么是協(xié)程,協(xié)程得好處等知識(shí)點(diǎn),需要的朋友可以參考一下
    2022-10-10
  • C語(yǔ)言數(shù)組全面詳細(xì)講解

    C語(yǔ)言數(shù)組全面詳細(xì)講解

    數(shù)組是一組有序的數(shù)據(jù)的集合,數(shù)組中元素類(lèi)型相同,由數(shù)組名和下標(biāo)唯一地確定,數(shù)組中數(shù)據(jù)不僅數(shù)據(jù)類(lèi)型相同,而且在計(jì)算機(jī)內(nèi)存里連續(xù)存放,地址編號(hào)最低的存儲(chǔ)單元存放數(shù)組的起始元素,地址編號(hào)最高的存儲(chǔ)單元存放數(shù)組的最后一個(gè)元素
    2022-05-05

最新評(píng)論