std::shared_ptr
智慧指標是c++11一個相當重要的特性,可以極大地將開發者從資源申請/釋放的繁重勞動中解放出來。
然而直到c++17前std::shared_ptr
都有一個嚴重的限制,那就是它並不支援動態陣列:
#include <memory>
std::shared_ptr<int[]> sp1(new int[10]()); // 錯誤,c++17前不能傳遞陣列型別作為shared_ptr的模板引數
std::unique_ptr<int[]> up1(new int[10]()); // ok, unique_ptr對此做了特化
std::shared_ptr<int> sp2(new int[10]()); // 錯誤,可以編譯,但會產生未定義行為,請不要這麼做
sp1
錯誤的原因很明顯,然而sp2
的就沒有那麼好找了,究其原因,是因為std::shared_ptr
對非陣列型別都使用delete p
釋放資源,顯然這對於new int[10]
來說是不對的,對它應該使用delete [] p
。
其實c++17前的解決方案並不複雜,我們可以藉助std::default_delete
,它用於提供對應型別的正確的delete操作:
std::shared_ptr<int> sp3(new int[10](), std::default_delete<int[]>());
現在我們提供了正確的delete操作,可以放心地使用了。
不過這麼做的缺點也是很明顯的:
- 我們想管理的值是int[]型別的,然而事實上傳給模板引數的是int
- 需要顯示提供delete functor
- 不能使用
std::make_shared
,無法保證異常安全 - c++17前shared_ptr未提供
opreator[]
,所以當需要類似操作時不得不使用sp3.get()[index]
的形式
事實上共享一片連續分配記憶體的需求是極為常見的,所以為了修正上述缺陷,c++17以及即將推出的c++2a對std::shared_ptr
做了完善。
先說c++17的改進,shared_ptr增加了opreator[]
,並可以使用int[]
類的陣列型別做模板引數,所以sp3
的定義可以簡化了:
std::shared_ptr<int[]> sp3(new int[10]());
對於訪問分配的空間,可以將sp3.get()[index]
替換為sp3[index]
。看個具體的例子:
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int[]> sp(new int[5]());
for (int i = 0; i < 5; ++i) {
sp[i] = (i+1) * (i+1);
}
for (int i = 0; i < 5; ++i) {
std::cout << sp[i] << std::endl;
}
}
我們分配一個有5個int元素的動態陣列,然後分別賦值1-5的平方,然後輸出:
g++ -Wall -std=c++17 test.cpp
./a.out
1
4
9
16
25
使用被極大得簡化了,然而還是有點問題,那就是無法使用std::make_shared
,而我們除非指定自己的delete functor,否則我們應該儘量使用std::make_shared
。
所以c++20對此做了改進:
auto up2 = std::make_unique<int[]>(10); // 從c++14開始,分配一個管理有10個int元素的動態陣列的unique_ptr
// c++2a中你可以這樣寫,與上一句相似,只不過返回的是shared_ptr
auto sp3 = std::make_shared<int[]>(10);
在我的編譯器上(GCC 8.2.1)還不能支援這一特性,所以很遺憾得不能提供演示了。
不過等c++2a(很可能就叫c++20)釋出後std::shared_ptr
就能安全而便捷地管理動態陣列了。