轉自:https://blog.csdn.net/cpp_learner/article/details/118912592,chatgpt
1.介紹
智慧指標就是幫管理動態分配的記憶體的,它會幫助我們自動釋放new出來的記憶體,從而避免記憶體洩漏!使用智慧指標可以自動呼叫物件的解構函式。
例子:
class Test { public: Test() { cout << "Test的建構函式..." << endl; } ~Test() { cout << "Test的解構函式..." << endl; } int getDebug() { return this->debug; } private: int debug = 20; }; int main() { //Test *test = new Test; shared_ptr<Test> test(new Test); cout << "test->debug:" << test->getDebug() << endl; cout << "(*test).debug:" << (*test).getDebug() << endl; return 0; } //輸出結果 Test的建構函式... test->debug:20 (*test).debug:20 Test的解構函式...
智慧指標可以像普通指標一樣使用-> 和*取內容,是因為 shared_ptr過載了這2個運算子函式:
1.1 get方法
獲取智慧指標託管的指標地址。但一般不這樣用,直接用智慧指標操作即可。
// 定義智慧指標 shared_ptr<Test> test(new Test); Test *tmp = test.get(); // 獲取指標返回 cout << "tmp->debug:" << tmp->getDebug() << endl; // 函式原型 _NODISCARD _Ty * get() const noexcept { // return wrapped pointer return (_Myptr);// 直接返回託管的指標 }
1.2 release方法
取消智慧指標對動態記憶體的託管。
// 定義智慧指標 shared_ptr<Test> test(new Test); Test *tmp2 = test.release(); // 取消智慧指標對動態記憶體的託管 delete tmp2; // 之前分配的記憶體需要自己手動釋放 // 函式原型 _Ty * release() noexcept { // return wrapped pointer and give up ownership _Ty * _Tmp = _Myptr; _Myptr = nullptr;// 將成員指標設定為空 return (_Tmp);// 將指標返回外界,由外界來控制是否delete }
1.3 reset方法
重置智慧指標託管的記憶體地址,如果地址不一致,原來的會被析構掉。
// 重置 p.reset() ; 將p重置為空指標,所管理物件引用計數 減1 p.reset(p1); 將p重置為p1(的值),p 管控的物件計數減1,p接管對p1指標的管控 p.reset(p1,d); 將p重置為p1(的值),p 管控的物件計數減1並使用d作為刪除器 p1是一個指標! // 交換 std::swap(p1,p2); // 交換p1 和p2 管理的物件,原物件的引用計數不變 p1.swap(p2); // 交換p1 和p2 管理的物件,原物件的引用計數不變
2.shared_ptr 介紹
記錄引用特定記憶體物件的智慧指標數量,當複製或複製時,引用計數加1,當智慧指標析構時,引用計數減1,如果計數為零,代表已經沒有指標指向這塊記憶體,那麼我們就釋放它。
class Person { public: Person(int v) { this->no = v; cout << "建構函式 \t no = " << this->no << endl; } ~Person() { cout << "解構函式 \t no = " << this->no << endl; } private: int no; }; int main() { shared_ptr<Person> sp1; shared_ptr<Person> sp2(new Person(2)); // 獲取智慧指標管控的共享指標的數量 use_count():引用計數 cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; // 共享 sp1 = sp2; cout << "sp1 use_count() = " << sp1.use_count() << endl; cout << "sp2 use_count() = " << sp2.use_count() << endl << endl; shared_ptr<Person> sp3(sp1); // 共同託管 cout << "sp1 use_count() = " << sp1.use_count() << endl; // 可以列印引用計數值 cout << "sp2 use_count() = " << sp2.use_count() << endl; cout << "sp2 use_count() = " << sp3.use_count() << endl << endl; return 0; } // 輸出結果 建構函式 no = 2 sp1 use_count() = 0 sp2 use_count() = 1 sp1 use_count() = 2 sp2 use_count() = 2 sp1 use_count() = 3 sp2 use_count() = 3 sp2 use_count() = 3 解構函式 no = 2
2.1 使用make_shared構建物件
make_shared<型別>(建構函式引數列表); shared_ptr<int> up3 = make_shared<int>(2); // 多個引數以逗號','隔開,最多接受十個 shared_ptr<string> up4 = make_shared<string>("字串"); shared_ptr<Person> up5 = make_shared<Person>(9); //()內是建構函式的引數
會將物件指標和引用計數指標分配在一起,更高效。
ref_count也需要一塊堆空間,(這裡vptr是什麼?虛擬函式表的指標?為什麼會放到這裡?)
shared_ptr<Foo> x(new Foo);
這種方式需要為 Foo 和 ref_count 各分配一次記憶體,而使用
shared_ptr<Foo> x=make_shared<Foo>();
會一次分配較大的記憶體供 Foo 和 ref_count 物件容身,(存放到一塊地址空間)。
3.注意事項
- 在呼叫p.release()時會返回該指標,但不會釋放p所指的記憶體,這時返回值就是對這塊記憶體的唯一索引,如果沒有使用這個返回值釋放記憶體或是儲存起來,這塊記憶體就洩漏了。
- 禁止delete 智慧指標get 函式返回的指標。析構時造成重複釋放。
- 禁止用任何型別智慧指標get 函式返回的指標去初始化另外一個智慧指標。具體解釋:
//有問題的程式碼 std::shared_ptr<int> sp1(new int(10)); // 管理一個動態分配的整數 std::shared_ptr<int> sp4(sp1.get()); // 直接使用裸指標構造
sp1.get()
返回一個指向動態分配的整數的裸指標,但 shared_ptr
並不共享控制塊和引用計數資訊。如果 shared_ptr
透過裸指標構造,兩個 shared_ptr
將獨立管理同一個指標物件。這會導致兩個 shared_ptr
在各自的生命週期結束時嘗試釋放相同的動態分配物件,從而造成雙重釋放問題,導致未定義行為(通常程式崩潰)。正確用法:
auto sp1 = std::make_shared<int>(10); auto sp4 = sp1; // 正確: sp4 現在共享 sp1 的控制塊和引用計數
透過複製建構函式來共享物件的所有權,確保引用計數正確管理物件的生命週期。
- 不要定義指向智慧指標的指標,這違背了智慧指標的初衷。
4.和const
4.1 const std::shared_ptr<T>
指標本身是常量,即你不能修改 shared_ptr 物件(例如,不能讓它指向另一個物件),但透過這個指標可以修改它指向的物件。
const shared_ptr<string> up4 = make_shared<string>("mystr"); // 報錯: error: no match for ‘operator=’ (operand types are ‘const std::shared_ptr<std::__cxx11::basic_string<char> >’ and ‘std::shared_ptr<std::__cxx11::basic_string<char> >’) up4 = make_shared<string>("mystr2");// 該行程式碼會報錯,不能修改 `up4` 本身 *up4 = "mmmm"; cout<<*up4;// 輸出mmmm,可以修改指向的內容
4.2 std::shared_ptr<const T>
指指標本身是可變的(即可以修改指標使其指向另一個物件),但不能修改它指向的物件,即所指物件是常量。當你希望透過 shared_ptr 提供只讀訪問許可權時,可以使用 std::shared_ptr<const T>。
shared_ptr<const string> up4 = make_shared<string>("mystr"); up4 = make_shared<string>("mystr2");// 可以更改指向新的物件 //error: assing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers *up4 = "mmmm";//報錯,不能修改 cout<<*up4;