C++ 筆記:動態記憶體和智慧指標
阿新 • • 發佈:2018-12-13
- 物件的生存期:
- 全域性物件:程式啟動時建立,程式結束時銷燬
- 區域性自動物件:進入物件所在程式塊時建立,離開程式塊時銷燬
static
物件:第一次使用時建立,程式結束時銷燬- 動態分配的物件:建立之後只有在顯式地被釋放時才會銷燬
- 每個程式擁有一個記憶體池,稱為自由空間或堆,用來儲存動態分配的物件
智慧指標
- 傳統的動態記憶體管理:
new
:在動態記憶體中為物件分配空間,返回一個指向該物件的指標delete
:接受一個動態物件的指標,銷燬該物件,釋放記憶體
- 智慧指標:
#include <memory>
- 智慧指標是模板類,建立智慧指標時需要提供具體的型別
shared_ptr
unique_ptr
:“獨佔”所指向的物件shared_ptr
和unique_ptr
都支援的操作
shared_ptr<T> sp_T; // 空智慧指標
unique_ptr<T> up_T;
*sp_T, *up_T // 解引用
sp_T->mem, up_T->mem // 成員訪問
swap(p1, p2), p1.swap(p2) // 交換 p1 和 p2
shared_ptr
shared_ptr<int> sp_int;
shared_ptr< vector<string> sp_str_vec;
shared_ptr
獨有的操作
make_shared<T>(args) // 返回一個 shared_ptr,指向一個動態分配的 T 型別物件,並使用 args 初始化該物件
shared_ptr<T>sp2(sp1) // sp2 為 sp1 的拷貝;遞增 sp1 指向的物件的計數器
sp2 = sp1 // 遞減 sp2 原來所指向的物件的引用計數,遞增 sp1 指向的物件的引用計數;
// 若 sp2 原來所指向的物件的引用計數變為 0,則釋放該物件
sp.unique() // 若 sp.use_count() 為 1,則返回 true,否則返回 false
sp.use_count() // 返回與 sp 共享物件的智慧指標數量
make_shared<T>(args)
- 最安全的分配和使用動態記憶體的方法
- 在記憶體中分配一個
T
型別的物件,使用args
初始化該物件,並返回指向該物件的shared_ptr
shared_ptr<int> sp_int = make_shared<int>(1024);
shared_ptr<string> sp_str = make_shared<string>("Hello, world!");
shared_ptr<vector<double>> sp_dbl_vec = make_shared<vector<double>>(vector<double>({3.14159, 1.414, 1.732}));
// 也可以使用 auto
list<int> int_lst{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto sp_int_lst = make_shared<list<int>>(int_lst);
shared_ptr
的賦值和拷貝
- 進行賦值和拷貝時,每個
shared_ptr
都會記錄指向相同物件的其他shared_ptr
的數量 - 每進行一次賦值和拷貝,
shared_ptr
指向的物件的引用計數都會遞增 - 當給
shared_ptr
賦予一個新值,或是shared_ptr
被銷燬時,其原來所指向的物件的引用計數會遞減 - 當一個
shared_ptr
指向的物件的引用計數變為 0 時,便自動釋放該物件
auto sp_int1 = make_shared<int>(1024); // sp_int1 指向的物件只有 sp_int1 一個引用者
auto sp_int2 = sp_int1; // sp_int1 指向的物件有了兩個引用者 sp_int1 和 sp_int2
使用 shared_ptr
實現多個物件之間共享資料
- 一個物件的成員在物件銷燬時也會被銷燬;將資料儲存在動態記憶體中,可以實現物件銷燬時的資料保留
class StrBlob {
public:
typedef vector<string>::size_type size_type;
// constructors and destructor
StrBlob();
StrBlob(initializer_list<string> str_ilist);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string &str) { data->push_back(str); }
void pop_back();
string & front();
string & back();
shared_ptr<vector<string>> data;
private:
void check(size_type i, const string & msg) const;
};
// 預設建構函式,在記憶體中分配一個空的 vector<string>
StrBlob::StrBlob() : data(make_shared<vector<string>>()) {}
// 建構函式,在記憶體中分配所需的空間
StrBlob::StrBlob(initializer_list<string> str_ilist) : data(make_shared<vector<string>>(str_ilist)) {}
void StrBlob::check(size_type i, const string & msg) const {
if (i >= data->size())
throw out_of_range(msg);
}
string & StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
string & StrBlob::back() {
check(0, "front on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
int main()
{
StrBlob strblob1;
{
StrBlob strblob2({ "Hello, ", "welcome ", "to ", "the ", "C++ ", "world." });
cout << strblob2.data << " : " << strblob2.front() << endl; // output: 0328E30C : Hello,
strblob1 = strblob2;
cout << strblob1.data << " : " << strblob1.front() << endl; // output: 0328E30C : Hello,
} // strblob2 被銷燬,但 strblob2.data 原來指向的記憶體空間依然有效,並被 strblob1.data 所指
cout << strblob1.data << " : " << strblob1.front() << endl; // output: 0328E30C : Hello,
cin.get();
return 0;
}
unique_ptr
- 一個
unique_ptr
“獨佔”它所指向的記憶體,也就是說,在任一時刻,最多隻能有一個unique_ptr
指向一個給定物件 - 當一個
unique_ptr
被銷燬時,其所指的物件也被銷燬
unique_ptr
獨有的操作
unique_ptr<T> up1; // 空指標,可以指向 T 型別的物件
unique_ptr<T, D> up2; // up1 使用 delete 釋放,up2 則使用型別為 D 的可呼叫物件釋放
unique_ptr<T, D> up3(d); // up3 使用型別為 D 的物件 d 釋放
up = nullptr; // 釋放 up 指向的物件,並將 up 置空
up.release(); // up 放棄對指標的控制權,返回指標,並將 up 置空
up.reset(); // 釋放 up 指向的物件
up.reset(q); // 如果提供了內建指標 q,則令 up 指向這個物件;否則將 up 置空
up.reset(nullptr);
- 定義一個
unique_ptr
時,需要將其繫結到一個 new 返回的指標 unique_ptr
不支援普通的賦值和拷貝操作
unique_ptr<double> up_dbl;
unique_ptr<int> up_int(new int(1024));
unique_ptr<string> up_str1(new string("Hello, world!"));
unique_ptr<vector<string>> up_str_vec(new vector<string>({ "Hello, ", "world!" }));
unique_ptr<string> up_str2(up_str1.release()); // 將所有權從 up_str1 轉移給 up_str2,
// release() 將 up_str1 置空
unique_ptr<string> up_str3(new string("Welcome to the C++ world."));
up_str2.reset(up_str3.release()); // 將所有權從 up_str3 轉移給 up_str2,
// 之後 reset() 將 up_str2 釋放