C ++ 98のスマートポインタauto_ptrは以前に導入されましたが、次にその代替[unique_ptr]を紹介しましょう。
それを読んだ後、私も酔っていました。続けましょう
auto_ptrは、C ++ 11より前に使用されていたスマートポインターです。auto_ptrは排他的所有権モデルに基づいているため、2つのポインターが同じリソースを指すことはできないため、コピーまたは割り当てによってリソースの所有権が変更されます。auto_ptrには3つの主な問題があります。
- コピーと割り当てにより、リソースの所有権が変更されますが、これは人間の直感に沿ったものではありません。
- STLコンテナでauto_ptrを使用することには大きなリスクがあります。これは、コンテナ内の要素がコピーの構築可能および割り当て可能をサポートする必要があるためです。
- オブジェクト配列の操作をサポートしていません
最初にauto_ptrのリスクを見てみましょう
#include <iostream>
#include <memory>
#include <vector>
#include <Windows.h>
#include <string>
#include <stdio.h>
using namespace std;
int
main(int argc, const char* argv[])
{
auto_ptr<string> str_ptr1(new string("Libero"));
auto_ptr<string> str_ptr2(new string("Martin"));
// 被C++11 抛弃的主要理由 p1= p2 ,复制或赋值都会改变资源的所有权
printf("str_ptr1: %p\n", str_ptr1.get());
printf("str_ptr2: %p\n", str_ptr2.get());
printf("after str_ptr1 = str_ptr2;\n");
str_ptr1 = str_ptr2;
printf("str_ptr1: %p\n", str_ptr1.get());
printf("str_ptr2: %p\n", str_ptr2.get());
//弊端2. 在 STL 容器中使用auto_ptr存在重大风险,
//因为容器内的元素必需支持可复制(copy constructable)和可赋值(assignable)。
vector<auto_ptr<string>> va_ptr;
auto_ptr<string> str_ptr3(new string("I am Libero"));
auto_ptr<string> str_ptr4(new string("I am Martin"));
va_ptr.push_back(std::move(str_ptr3));
va_ptr.push_back(std::move(str_ptr4));
for (auto itor = va_ptr.cbegin(); itor != va_ptr.cend(); ++itor) {
cout << *(*itor) << endl;
}
// 风险来了
va_ptr[0] = va_ptr[1];
cout << "va_ptr[0]: " << *va_ptr[0] << endl;
cout << "va_ptr[1]: " << *va_ptr[1] << endl;
//弊端3. 不支持对象数组的内存管理
//auto_ptr<int[]> ai(new int[5]); //不能这样定义
system("pause");
return 0;
}
auto_ptrを見た後、unique_ptrを見てみましょう
unique_ptrの特性
- 排他的所有モデルに基づく:2つのポインターが同じリソースを指すことはできません
- 値を残すことはできませんunique_ptrコピーコンストラクターを値を残すことはできませんコピーの割り当てですが、一時的な 右辺値の割り当て構造と割り当ては可能です
- オブジェクトへのポインタを保存し、スコープを離れると、オブジェクトが指すオブジェクトを自動的に解放します。
- ポインタをコンテナに保存しても安全です
unique_ptr<string> str_ptr1(new string("Libero"));
unique_ptr<string> str_ptr2(new string("Martin"));
printf("str_ptr1: %p\n", str_ptr1.get());
printf("str_ptr2: %p\n", str_ptr2.get());
printf("after str_ptr1 = str_ptr2;\n");
str_ptr1 = std::move(str_ptr2); // 必须要把 左值转成右值【就非常的nice,很细节】
printf("str_ptr1: %p\n", str_ptr1.get());
printf("str_ptr2: %p\n", str_ptr2.get());
unique_ptr<string> str_ptr3(new string("I am Libero"));
unique_ptr<string> str_ptr4(std::move(str_ptr3));
vector<unique_ptr<string>> vu_ptr;
vu_ptr.push_back(std::move(str_ptr3));
vu_ptr.push_back(std::move(str_ptr4));
for (auto itor = vu_ptr.cbegin(); itor != vu_ptr.cend(); ++itor) {
cout << (*itor).get() << endl;
}
unique_ptr<int[]> ui(new int[5]); //自动会调用 delete []函数去释放
効果:
思考:auto_ptrのstr_ptr1 = str_ptr2およびunique_ptrのstr_ptr1 = std :: move(str_ptr2)よりも優れているのはなぜですか?
ええと、str_ptr2の_Myptrを常に空のままにしておかないのですか、なぜ後者の方が良いのですか?
後者のstd :: move操作はプログラマーに思い出させるだけでなく、この操作はstr_ptr2の元の_Myptrを無効にするので、後でstr_ptr2を引用しないでください!さらに、これは左辺値と右辺値の特性とも一致しています。std:: move()を追加すると、これは一時的な値であり、後で使用できないことを意味します。この前に、誰もが何が左の値で何が右の値であるかを理解する必要があります。左辺値参照操作の特性、右辺値参照操作の特性。
unique_ptrのソースコードがわからない場合は、次の関数を参照できます。
コンストラクタ
unique_ptr <T> up; //空のunique_ptrは、タイプTのオブジェクトを指すことができます
unique_ptr <T> up1(new T()); // unique_ptrを定義し、タイプTのオブジェクトをポイントします
unique_ptr <T []> up; //空のunique_ptrは、タイプT [の配列オブジェクトを指すことができます。
unique_ptr <T []> up1(new T []); // unique_ptrを定義し、タイプTの配列オブジェクトをポイントします
unique_ptr <T、D> up(); //空のunique_ptr、Dタイプの削除機能dを受け入れ、dを使用してメモリを解放します
unique_ptr <T、D> up(new T()); // unique_ptrを定義し、タイプTのオブジェクトをポイントし、タイプDのdeleter dを受け入れ、deleterdを使用してメモリを解放します。
オブジェクト制御を放棄する
up.release(); //オブジェクトの制御を放棄し、ポインタを返し、空に設定すると、メモリは解放されません
リセット
up.reset(...); //パラメータは空の組み込みポインタで、最初にupが指すオブジェクトを解放してから、upの値をリセットできます。
両替
up.swap(up1); //スマートポインタを上に交換し、up1によって制御されるオブジェクトを交換します