智能指標#
C++ 中的智能指標是一種用於管理動態分配的物件的指標。它們提供了自動記憶體回收的功能,可以幫助避免記憶體洩漏和懸空指標的問題。C++ 標準庫提供了兩種主要的智能指標:std::unique_ptr
和std::shared_ptr
。
std::unique_ptr
:
std::unique_ptr
是一種獨佔所有權的智能指標。它確保只有一個指標可以訪問給定的資源。當std::unique_ptr
超出範圍或被刪除時,它會自動釋放所管理的物件。它不能被複製,但可以通過移動語義傳遞所有權。使用std::make_unique
函數可以方便地創建std::unique_ptr
物件。
{
std::unique_ptr<int> ptr = std::make_uniqure<int>(42);
std::cout << *ptr << std::endl;
}
// ptr 超出範圍後,其管理的物件會被自動釋放
std::shared_ptr
:
std::shared_ptr
是一種共享所有權的智能指標。它可以被多個指標共享,並且會跟踪有多少個指標引用了給定的資源。只有當最後一個std::shared_ptr
超出範圍或被刪除時,才會釋放所管理的物件。它可以通過複製來共享所有權,也可以通過移動語義傳遞所有權。使用std::make_shared
函數可以方便地創建std::shared_ptr
物件。
{
std::unique_ptr ptr1 = std::make_uniqure(42);
std::unique_ptr ptr2 = ptr1; // 共享所有權
}
//ptr1 和 ptr2 超出範圍後,其管理的物件會被自動釋放
智能指標的使用可以有效地管理動態分配的物件,避免記憶體洩漏和懸空指標的問題。但需要注意,智能指標並不能解決所有的記憶體管理問題,例如循環引用可能導致資源無法釋放。因此,在使用智能指標時,仍然需要謹慎設計和管理物件的生命週期。
關於共享指標,以 std::make_shared<Chunk>(position)
為例#
std::make_shared<Chunk>(position)
是一個使用 std::make_shared
函數創建 Chunk
物件的語法。
具體來說,這行程式碼使用了尖括號 <Chunk>
來指定模板參數,告訴 std::make_shared
函數我們要創建的物件的類型是 Chunk
。然後,括號中的 position
是傳遞給 Chunk
建構函數的參數。
std::make_shared
是一個模板函數,它的定義如下:
template<typenameT, typename... Args>
shared_ptr<T> make_shared(Args&&... args);
它接受一個可變數量的參數 Args&&... args
,這些參數將被傳遞給 T
類型的建構函數來創建物件。在這個例子中,T
是 Chunk
類型,Args
是 position
的類型。
std::make_shared
函數返回一個 shared_ptr<T>
類型的共享指標,它指向通過傳遞的參數建構的 T
類型物件。
因此,std::make_shared<Chunk>(position)
這行程式碼的作用是創建一個 Chunk
類型的物件,並使用共享指標來管理該物件的生命週期。同時,將 position
作為參數傳遞給 Chunk
的建構函數,以便在創建物件時進行初始化。
實際上,std::make_shared
主要用於構造新的物件。它會在堆上動態分配記憶體,並使用傳遞的參數來調用物件的建構函數。
如果有一個已經存在的實例,並且想要將其包裝在一個共享指標中進行管理,可以使用 std::shared_ptr
的建構函數或 std::make_shared
的變體來實現。
使用 std::shared_ptr
的建構函數可以將一個已經存在的指標包裝成共享指標。#
例如:
Chunk* existingChunk = newChunk(position);
std::shared_ptr<Chunk> sharedChunk(existingChunk);
在這個例子中,existingChunk
是一個已經存在的 Chunk
物件的指標,通過將它傳遞給 std::shared_ptr
的建構函數,可以創建一個共享指標 sharedChunk
來管理該物件的生命週期。
在這種情況下,不應該繼續使用
existingChunk
,因為一旦sharedChunk
被銷毀(或者最後一個擁有它的共享指標被銷毀),它指向的記憶體會被自動釋放。如果在那之後嘗試訪問existingChunk
,將會導致未定義行為,比如訪問已經釋放的記憶體。
(不推薦的!)
另一種方法是使用 std::make_shared
的變體,即 std::allocate_shared
:
Chunk* existingChunk = newChunk(position);
std::shared_ptr<Chunk> sharedChunk = std::allocate_shared<Chunk>(std::allocator<Chunk>(), *existingChunk);
在這個例子中,使用 std::allocate_shared<Chunk>(std::allocator<Chunk>(), *existingChunk)
會創建一個新的 Chunk
實例,並且這個新實例是通過複製 existingChunk
指向的物件來初始化的。這裡並沒有發生將普通指標 existingChunk
轉換為共享指標的行為,而是發生了物件的複製。std::allocate_shared
使用提供的物件(即 *existingChunk
)作為複製建構函數的參數來創建新物件,並由返回的共享指標管理。
通過 std::allocate_shared
創建一個物件的副本並同時保留原始指標指向的物件 —— 通常是不推薦的行為。這種做法會導致以下幾個問題:
-
記憶體管理複雜化:這種方法創建了兩個獨立的物件實例,一個由原始指標管理,另一個由共享指標管理。這就需要你分別管理這兩個物件的生命週期,增加了記憶體洩漏的風險。
-
設計意圖不明確:智能指標(如 std::shared_ptr)的引入主要是為了簡化記憶體管理,通過自動管理物件的生命週期來減少記憶體洩漏和懸垂指標的風險。通過複製物件而不是接管原始指標,你實際上沒有利用智能指標的主要優勢。
-
效能開銷:複製物件可能涉及顯著的效能開銷,尤其是當物件較大或複製操作代價高昂時。如果沒有必要創建物件的副本,這種開銷是可以避免的。