送給大家一句話:
世界在旋轉(zhuǎn),我們跌跌撞撞前進(jìn),這就夠了 —— 阿貝爾 加繆
1 前言
我們之前實(shí)現(xiàn)了手搓vector,但是當(dāng)時(shí)依然有些問(wèn)題沒有解決:
- 迭代器區(qū)間拷貝(非法的間接尋址問(wèn)題)
- 迭代器失效問(wèn)題
- 使用memcpy拷貝問(wèn)題
接下來(lái),我們一點(diǎn)一點(diǎn)來(lái)解決這些問(wèn)題?。?!
2 迭代器區(qū)間拷貝
來(lái)看這個(gè)這個(gè)構(gòu)造函數(shù):
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
first++;
}
}
這個(gè)是對(duì)迭代器區(qū)間進(jìn)行的構(gòu)造函數(shù),思路很簡(jiǎn)單,把迭代器區(qū)間的數(shù)據(jù)依次尾插就可以了(這里之所以另外使用一個(gè)新的模版,而不是使用vector類的模版,是為了兼容更多的數(shù)據(jù)類型)。這樣就可以通過(guò)一個(gè)現(xiàn)有的類型來(lái)構(gòu)造容器。
但是出乎意料的是出現(xiàn)了一個(gè)問(wèn)題: C2100 非法的間接尋址
(編譯層面的問(wèn)題) 。非法的間接尋址的造成原因有很多:
- 空指針引I用:當(dāng)一個(gè)指針沒有被初始化或者為NULL時(shí),對(duì)它進(jìn)行間接尋址操作會(huì)導(dǎo)致非法訪問(wèn)。
- 野指針引用:當(dāng)一個(gè)指針超出了它所指向的內(nèi)存范圍,或者已經(jīng)被釋放但仍然被引用時(shí),進(jìn)行間接尋址操作也會(huì)導(dǎo)致非法訪問(wèn)。
- 類型不匹配:如果試圖將指針轉(zhuǎn)換為不兼容的類型進(jìn)行間接尋址,也會(huì)導(dǎo)致非法訪問(wèn)。
我們分析一下我們遇到的問(wèn)題是那種問(wèn)題?空指針引用嗎?不可能!野指針引用嗎?也不可能!??! 那么真相只有一個(gè):我們遇到了類型不匹配
的問(wèn)題,那這是來(lái)自哪里的呢???,經(jīng)過(guò)我的排除法(注釋不同的代碼塊來(lái)進(jìn)行查找),得到了結(jié)果
vector<int> v1(5,6);
這一行代碼是我們出錯(cuò)的根源,為什么這個(gè)構(gòu)造沒有去使用vector(size_t n,T val = T())
,而是使用我們的vector(InputIterator first, InputIterator last)
,因?yàn)榈诙€(gè)函數(shù)與(5,6)的類型更匹配,編譯器會(huì)尋找最合適的函數(shù)。
解決方法也是十分暴力:多枚舉幾個(gè) 構(gòu)造函數(shù):
vector(size_t n,T val = T())
vector(int n,T val = T());
vector(long long n,T val = T());
這樣就會(huì)優(yōu)先匹配vector(int n,T val = T());
了,我們的問(wèn)題也就解決了。
3 迭代器失效問(wèn)題
這個(gè)問(wèn)題主要出現(xiàn)在我們的插入操作(insert)和刪除操作(erase)。來(lái)看:
void vector_test7() {
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
vector<int>::iterator it = v1.begin() + 3;// 4
cout << *it << endl;
v1.insert(3, 40);
cout << *it << endl;
}
這個(gè)執(zhí)行的結(jié)果是:
迭代器的指向發(fā)生了改變,我們實(shí)現(xiàn)的迭代器的底層是指針,我們插入之后指針位置不變,而數(shù)組元素改變,自然會(huì)產(chǎn)生不一樣的結(jié)果。這個(gè)問(wèn)題看起來(lái)不嚴(yán)重,那我們?cè)賮?lái)看:
void vector_test7() {
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
v1.push_back(8);
vector<int>::iterator it = v1.begin() + 3;// 4
cout << *it << endl;
v1.insert(3, 40);
cout << *it << endl;
}
為什么這里出現(xiàn)了亂碼???我們代碼和之前的區(qū)別是什么???一個(gè)進(jìn)行了擴(kuò)容,一個(gè)沒進(jìn)行擴(kuò)容。擴(kuò)容之后vector的_start發(fā)生了改變,自然我們的指針也失去了對(duì)應(yīng)作用。 迭代器就失效了,這個(gè)解決辦法也很簡(jiǎn)單,就是插入之后不要使用之前的迭代器?。?!一定要對(duì)迭代器進(jìn)行更新。
再來(lái)看erase中的問(wèn)題:
void vector_test7() {
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
//v1.push_back(6);
//v1.push_back(7);
//v1.push_back(8);
vector<int>::iterator it = v1.begin();
//刪除偶數(shù)
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
print_vector(v1);
}
這樣運(yùn)行起來(lái)是沒有問(wèn)題的,那么再來(lái)看:
void vector_test7() {
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(4);
v1.push_back(5);
//v1.push_back(6);
//v1.push_back(7);
//v1.push_back(8);
vector<int>::iterator it = v1.begin();
//刪除偶數(shù)
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
++it;
}
print_vector(v1);
}
現(xiàn)在出現(xiàn)了:
這個(gè)問(wèn)題,問(wèn)題的來(lái)源也很簡(jiǎn)單,我們迭代器在刪除之后沒有改變位置,但是_start的元素發(fā)生了改變,也就是相當(dāng)于 it 向后移動(dòng)了兩次,為了避免這個(gè)情況我們可以:
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
else
{
++it;
}
}
這樣就可以了:
需要注意的一點(diǎn)是,我們的操作是以g++標(biāo)準(zhǔn)來(lái)進(jìn)行的(如果刪除會(huì)進(jìn)行縮容,也會(huì)出現(xiàn)錯(cuò)誤,迭代器就不能進(jìn)行++了),所以 在VS環(huán)境下,vector 容器在erase 之后的迭代器是嚴(yán)格不能使用的,使用就會(huì)報(bào)錯(cuò)
,因?yàn)閂S迭代器的底層不是原生指針,判斷有所不同。
迭代器失效解決方案總結(jié):
1. 刪除插入之后更新對(duì)應(yīng)迭代器?。╡rase刪除后會(huì)返回新的迭代器 ,按規(guī)則進(jìn)行迭代就可以了 it = v1.erase(it)
)
2. 插入刪除之后不使用迭代器
4 memcpy拷貝問(wèn)題
我們創(chuàng)建一個(gè)string類的容器,來(lái)看看能不能正常運(yùn)行:
void vector_test8() {
vector<string> v1;
v1.push_back("11111");
v1.push_back("22222");
v1.push_back("33333");
v1.push_back("44444");
v1.push_back("55555");
print_vector(v1);
}
來(lái)看效果:程序直接崩掉了,經(jīng)過(guò)我們的調(diào)試,我們能打印出來(lái)正確的數(shù)據(jù),但是走到程序最后的時(shí)候出現(xiàn)了錯(cuò)誤,那么應(yīng)該就是析構(gòu)函數(shù)的問(wèn)題了!
來(lái)畫圖分析一波:
- memcpy是內(nèi)存的二進(jìn)制格式拷貝,將一段內(nèi)存空間中內(nèi)容原封不動(dòng)的拷貝到另外一段內(nèi)存空間中
- 如果拷貝的是自定義類型的元素,memcpy既高效又不會(huì)出錯(cuò),但如果拷貝的是自定義類型元素,并且自定義類型元素中涉及到資源管理時(shí),就會(huì)出錯(cuò),因?yàn)?strong>memcpy的拷貝實(shí)際是淺拷貝。
結(jié)論:如果對(duì)象中涉及到資源管理時(shí),千萬(wàn)不能使用memcpy進(jìn)行對(duì)象之間的拷貝,因?yàn)閙emcpy是淺拷貝,否則可能會(huì)引起內(nèi)存泄漏甚至程序崩潰
那么怎么解決呢???非常簡(jiǎn)單:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-845306.html
//擴(kuò)容
void reserve(size_t newcapacity) {
//記錄位置
size_t n = _finish - _start;
T* tmp = new T[newcapacity];
//拷貝
//memcpy(tmp, _start, size() * sizeof(T));
for (size_t i = 0; i < size(); i++)
{
tmp[i] = _start[i];
}
delete[] _start;
_start = tmp;
_finish = _start + n;
_end = _start + newcapacity;
}
不使用memcpy函數(shù)不就可以了,然后我們使用簡(jiǎn)單粗暴的賦值拷貝,這樣就不會(huì)發(fā)生淺拷貝問(wèn)題了!??!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-845306.html
到了這里,關(guān)于【C++】vector問(wèn)題解決(非法的間接尋址,迭代器失效 , memcpy拷貝問(wèn)題)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!