[英]C++: Why is the destructor being called here?
我想我不太了解C ++中的析構函數。 這是我編寫的用於重現此問題的示例程序:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Odp
{
int id;
Odp(int id)
{
this->id = id;
}
~Odp()
{
cout << "Destructing Odp " << id << endl;
}
};
typedef vector<shared_ptr<Odp>> OdpVec;
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
shpoutOdp.reset();
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp.reset(iter->get());
return true;
}
}
return false;
}
int main()
{
OdpVec vec;
vec.push_back(shared_ptr<Odp>(new Odp(0)));
vec.push_back(shared_ptr<Odp>(new Odp(1)));
vec.push_back(shared_ptr<Odp>(new Odp(2)));
shared_ptr<Odp> shOdp;
bool found = findOdpWithID(0, shOdp, vec);
found = findOdpWithID(1, shOdp, vec);
}
在main()
結束之前,該程序的輸出為:
Destructing Odp 0
Destructing Odp 1
為什么會這樣? 我保留對向量中每個Odp
實例的引用。 它與通過引用傳遞shared_ptr
有關系嗎?
更新我認為基於MSDN , shared_ptr::reset
減少了引用計數:
運營商都會減少* this當前擁有的資源的參考計數
但是也許我誤會了嗎?
更新2 :看起來此版本的findOdpWithID()
不會導致析構函數被調用:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp = *iter;
return true;
}
}
return false;
}
這條線可能是您的絆腳石。
shpoutOdp.reset(iter->get());
您在這里所做的是(通過get()
)從智能指針get()
裸指針,該指針上沒有任何參考跟蹤信息,然后告訴shpoutOdp
重置自身以指向裸指針。 當shpoutOdp
被破壞時,它不知道還有另一個shared_ptr
指向同一事物,並且shpoutOdp
繼續銷毀它指向的事物。
你應該做
shpoutOdp = *iter;
這樣可以正確維護參考計數。 順便說一句, reset()
確實遞減了參考計數器(並且僅在計數達到0時銷毀)。
幾乎正確使用了許多東西:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
這里的參數shpoutOdp是輸入參數的副本。 考慮到它是一個共享指針,這沒什么大不了的,但這可能不是您想要的。 您可能想通過引用傳遞,否則為什么要首先將其傳遞給函數。
shpoutOdp.reset();
在傳遞參數時對其進行重置。
這是否意味着它可能很臟(然后將其用作輸入參數),如果您要傳遞某些內容,它會使該函數返回一個共享指針。
Odp& odp = *(iter->get());
除非確實需要(並且也確實需要),否則不要在共享指針上使用get。 不需要提取指針就可以了解指針所指向的位置,因為您正在處理指針,因此更容易出錯。 等效的safe(r)行是:
Odp& odp = *(*iter); // The first * gets a reference to the shared pointer.
// The second star gets a reference to what the shared
//pointer is pointing at
這是所有錯誤的地方:
shpoutOdp.reset(iter->get());
您正在從一個指針創建一個新的共享指針。 不幸的是,該指針已經由另一個共享指針管理。 因此,現在您有兩個共享的指針,它們認為它們擁有該指針,並且在超出范圍時將其刪除(第一個在函數末尾超出范圍,因為它是輸入參數的副本(而是比參考))。 正確的事情就是做一個作業。 然后,共享的指針知道它們正在共享一個指針:
shpoutOdp = *iter; // * converts the iterator into a shared pointer reference
下一行雖然不是完全錯誤,但確實假定所使用的迭代器是隨機訪問的(對於矢量而言是正確的)。
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
但是,這會使代碼變得更加脆弱,因為對typedef的簡單更改OdpVec會在沒有任何警告的情況下破壞代碼。 因此,為了使其與常規迭代器用法更加一致,請在檢查end()時使用!=,並且更喜歡預增量運算符:
for (OdpVec::iterator iter = vec.begin(); iter != vec.end(); ++iter)
如果只想影響單個shared_ptr引用,只需為其分配即可。 shared_ptr::reset
破壞了shared_ptr
已經存在的內容。
編輯:為了回應評論,您可以通過將for循環的主體更改為以下內容來修復它:
if ((*iter)->id == id)
{
shpoutOdp = *iter;
return true;
}
EDIT2:所有人都說過,為什么不在這里使用std :: find_if?
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm> //for std::find_if
#include <functional> //for std::bind
struct Odp
{
int id;
int GetId()
{
return id;
}
Odp(int id)
{
this->id = id;
}
~Odp()
{
std::cout << "Destructing Odp " << id << std::endl;
}
};
typedef std::vector<shared_ptr<Odp> > OdpVec;
int main()
{
OdpVec vec;
vec.push_back(std::shared_ptr<Odp>(new Odp(0)));
vec.push_back(std::shared_ptr<Odp>(new Odp(1)));
vec.push_back(std::shared_ptr<Odp>(new Odp(2)));
OdpVec::iterator foundOdp = std::find_if(vec.begin(), vec.end(),
std::bind(std::equal_to<int>(), 0, std::bind(&Odp::GetId,_1)));
bool found = foundOdp != vec.end();
}
關於shared_ptr
是它在內部處理引用計數。 你並不需要手動遞增或遞減過它。 (這就是為什么shared_ptr
也不允許您這樣做的原因)
調用reset
,它只是將當前的shared_ptr
為指向另一個對象(或為null)。 這意味着現在在reset
之前對其指向的對象的引用減少了,因此從某種意義上說,ref計數器已減少。 但這不是調用遞減ref計數器的函數。
您根本不需要這樣做。 只要讓shared_ptr
超出范圍,它就可以減少引用計數。
這是RAII發揮作用的一個例子。
您需要管理的資源(在本例中為shared_ptr
指向的對象)已綁定到堆棧分配的對象( shared_ptr
本身),因此可以自動管理其壽命。 shared_ptr
的析構函數可確保在適當的時候釋放所指向的對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.