前言
這幾天在項目中發現base代碼中有個關于指針shared_ptr引用使用問題,現在把問題的原因和寫的demo給大家分享一下,供大家參考。
問題内容
代碼中有個全局資料存儲上下文資訊,供UI某查詢界面對查詢條件進翻頁分段查詢。
界面示意圖
背景系統收到UI的新查詢時,此時請求中标志是“新請求/舊請求”标記為新請求,背景則建立并存儲一個對應的“上下文資訊”,用于标記目前查詢條件的操作,随後到資料庫查詢對應條件的全量資料并存在背景系統記憶體中,背景系統向UI傳回一定個數的資料;如果是目前查詢的翻頁操作,UI也會發起查詢請求,此時請求中的标志為“舊請求”,背景系統需要找到對應的上下文資訊并在記憶體中進行資料讀取。
背景系統在“上下文資訊”中存儲了某個智能指針資料ptr,在背景的具體查詢函數中,新定義了這個類型的智能指針引用類型auto & temp存儲ptr資料,但是最後在使用完後,顯示執行了智能指針的釋放(temp=nullptr;),導緻原智能指針ptr資料被釋放.....
問題産生原因
把“智能指針shared_ptr”指派給新定義新指針變量引用類型後,引用計數并沒有增加, 引用變量設定為nullptr後,原智能指針内部資料也已經無效。
智能指針shared_ptr介紹
shared_ptr允許多個該智能指針共享“擁有”同一堆配置設定對象的記憶體,這通過引用計數(reference counting)實作,會記錄有多少個shared_ptr共同指向一個對象,一旦最後一個這樣的指針被銷毀,也就是一旦某個對象的引用計數變為0,這個對象會被自動删除。支援複制和指派操作。
- shared_ptr 類對象預設初始化為一個空指針。智能指針是模闆,建立智能指針必須提供類型資訊。
如:
shared_ptr<string> p1; // shared_ptr,可以指向 string
shared_ptr<list<int>> p2; // shared_ptr,可以指向 int 的 list
- 智能指針的使用和普通指針類似,解引用一個智能指針傳回它指向的對象,如過在條件判斷中使用它,則是檢測其是否為空。
- 當進行拷貝或指派操作時,每個shared_ptr都會記錄有多少個其他shared_ptr指向相同的對象。
- 每個shared_ptr都有一個關聯的計數器,通常稱其為引用計數:
對shared_ptr類進行拷貝時,計數器就會增加(用一個shared_ptr初始化另一個shared_ptr、或者它作為參數傳遞給一個函數以及作為函數的傳回值)它所關聯的計數器就會增加;
當我們讓shared_ptr指向另一個對象或shared_ptr對象銷毀時,原對象的計數器就會遞減,一旦一個shared_ptr的計數器為0,就會自動釋放該對象的記憶體。
c++引用
引用就是某一變量(目标)的一個别名,對引用的操作與對變量直接操作完全一樣。引用的聲明方法:類型辨別符 &引用名=目标變量名;
- 引用引入了對象的一個同義詞,定義引用的表示方法與定義指針相似,隻是用&代替了*。
- 對引用求位址,就是對目标變量求位址。
智能指針shared_ptr取引用的計數效果
- 把“智能指針shared_ptr”指派給新定義新指針變量引用類型後,引用計數并沒有增加, 引用變量設定為nullptr後,原智能指針内部資料也已經無效
- 把“智能指針shared_ptr”指派給新定義新指針變量後,引用計數增加,新指針設定為nullptr後,原智能指針内部資料不影響
(可以通過對下列代碼15行處 #if 後數值改成0或1進行驗證)
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr(new int(5));
std::cout << "count: " << ptr.use_count() << std::endl; // 列印引用計數, 1
// 擷取原始指針并進行指派操作
int *p = ptr.get();
*p = 10;
// 輸出結果,發現值已經被修改
std::cout << *ptr << std::endl; // 輸出 10
#if 1
//把“智能指針shared_ptr”指派給新定義新指針變量引用類型後,引用計數并沒有增加, 引用變量設定為nullptr後,原智能指針内部資料也已經無效
auto &t = ptr;
std::cout << "count: " << ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << t.get() << std::endl;//0x1331b20
std::cout << ptr.get() << std::endl;//0x1331b20
t = nullptr;// 顯式銷毀所指對象
std::cout << "count: " << ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << p << std::endl;//0x1331b20
std::cout << ptr << std::endl;//0
//std::cout << *ptr << std::endl;//異常
std::cout << ptr.get() << std::endl;//0
#else
//把“智能指針shared_ptr”指派給新定義新指針變量後,引用計數增加,新指針設定為nullptr後,原智能指針内部資料不影響
auto t = ptr;
std::cout << "count: " << ptr.use_count() << std::endl; // 列印引用計數, 2
std::cout << t.get() << std::endl;//0x2220b20
std::cout << ptr.get() << std::endl;//0x2220b20
t = nullptr;// 顯式銷毀所指對象
std::cout << "count: " << ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << p << std::endl;//0x2220b20
std::cout << ptr << std::endl;//0x2220b20
std::cout << *ptr << std::endl;//輸出 10
#endif
return 0;
}
我們再看看通過函數入參傳入智能指針的效果:
(可以通過對下列代碼11行處 #if 後數值改成0或1進行驗證)
#include <iostream>
#include <memory>
struct s_test_
{
std::shared_ptr<int> ptr;
};
void fun(s_test_ & s_temp)
{
#if 1
//智能指針shared_ptr取引用後,引用計數并沒有增加, 引用變量設定為nulls_temp.ptr後,原智能指針内部資料也已經無效
auto &t = s_temp.ptr;
int *p = t.get();
*p = 20;
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << t.get() << std::endl;//0x1331b20
std::cout << s_temp.ptr.get() << std::endl;//0x1331b20
t = nullptr;// 顯式銷毀所指對象
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << p << std::endl;//0x1331b20
std::cout << s_temp.ptr << std::endl;//0
//std::cout << *(s_temp.ptr) << std::endl;//異常
std::cout << s_temp.ptr.get() << std::endl;//0
#else
//定義新指針指向“智能指針shared_ptr”後,引用計數增加,新指針設定為nulls_temp.ptr後,原智能指針内部資料不影響
auto t = s_temp.ptr;
int *p = t.get();
*p = 20;
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 列印引用計數, 2
std::cout << t.get() << std::endl;//0x2220b20
std::cout << s_temp.ptr.get() << std::endl;//0x2220b20
t = nullptr;// 顯式銷毀所指對象
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 列印引用計數, 1
std::cout << p << std::endl;//0x2220b20
std::cout << s_temp.ptr << std::endl;//0x2220b20
std::cout << *(s_temp.ptr) << std::endl;//輸出 20
#endif
}
int main()
{
s_test_ s1;
s1.ptr = std::make_shared<int>(5);
std::cout << "s1.ptr count: " << s1.ptr.use_count() << std::endl; // 列印引用計數, 1
// 擷取原始指針并進行指派操作
int *p = s1.ptr.get();
*p = 10;
// 輸出結果,發現值已經被修改
std::cout << *(s1.ptr) << std::endl; // 輸出 10
fun(s1);
std::cout << *(s1.ptr) << std::endl; // 輸出 20
return 0;
}
demo驗證結果:
智能指針shared_ptr取引用
定義新指針指向“智能指針shared_ptr”