ホームページ >バックエンド開発 >C#.Net チュートリアル >C++11 の新機能スマート ポインター (shared_ptr/unique_ptr/weak_ptr)

C++11 の新機能スマート ポインター (shared_ptr/unique_ptr/weak_ptr)

高洛峰
高洛峰オリジナル
2017-01-23 14:00:531981ブラウズ

shared_ptrの基本的な使い方

shared_ptrは、参照カウントを使用して、参照先のオブジェクトを管理します。同じオブジェクトを指す新しいshared_ptrがある場合(shared_ptrのコピーなど)、参照カウントは1増加します。 shared_ptr がスコープ外になると、参照カウントは 1 ずつ減ります。参照カウントが 0 に達すると、マネージド メモリが解放されます。

これの利点は、プログラマが手動でメモリを解放するというプレッシャーから解放されることです。以前は、プログラムで例外を処理するには、ポインタを手動でクラスにカプセル化し、デストラクタを使用して動的に割り当てられたメモリを解放する必要がよくありましたが、現在では、このプロセスをshared_ptrに任せることができます。

通常、shared_ptr を取得するには make_shared を使用します。

cout<<"test shared_ptr base usage:"<<endl;
shared_ptr<string> p1 = make_shared<string>("");
if(p1 && p1->empty())
*p1 = "hello";
 
auto p2 = make_shared<string>("world");
cout<<*p1<<&#39; &#39;<<*p2<<endl;
 
cout<<"test shared_ptr use_count:"<<endl;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<endl;
 
auto p3 = p2;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;
p2 = p1;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;

shared_ptr と new

shared_ptr は、new 式によって返されるポインターを使用して初期化できます。

cout<<"test shared_ptr and new:"<<endl;
shared_ptr<int> p4(new int(1024));
//shared_ptr<int> p5 = new int(1024); // wrong, no implicit constructor
cout<<*p4<<endl;

ただし、新しい式によって返されるポインタをshared_ptrに割り当てることはできません。

さらに、new とshared_ptrを混合しないように特別な注意を払う必要があります!

void process(shared_ptr<int> ptr)
{
cout<<"in process use_count:"<<ptr.use_count()<<endl;
}
 
cout<<"don&#39;t mix shared_ptr and normal pointer:"<<endl;
shared_ptr<int> p5(new int(1024));
process(p5);
int v5 = *p5;
cout<<"v5: "<<v5<<endl;
 
int *p6 = new int(1024);
process(shared_ptr<int>(p6));
int v6 = *p6;
cout<<"v6: "<<v6<<endl;

上記のプログラムフラグメントは次のように出力します:

in process use_count:2
v5: 1024
in process use_count:1
v6 : 0
プロセス p6 が 2 回目に処理されると、shared_ptr の参照カウントが 1 になっていることがわかります。プロセスのスコープから離れると、対応するメモリが解放され、p6 はダングリング ポインタになります。

そのため、新しい式によって返されたポインターがshared_ptrによって管理されたら、通常のポインターを介してこのメ​​モリにアクセスしないでください。

shared_ptr.reset

shared_ptr は、reset メソッドを通じて別のオブジェクトを指すようにリセットできます。このとき、元のオブジェクトの参照カウントは 1 つ減ります。

cout<<"test shared_ptr reset:"<<endl;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 nt:"<<p3.use_count()<<endl;
p1.reset(new string("cpp11"));
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;
shared_ptr deleter

shared_ptr がオブジェクトを解放するときに呼び出される削除関数をカスタマイズできます。

void print_at_delete(int *p)
{
cout<<"deleting..."<<p<<&#39;\t&#39;<<*p<<endl;
delete p;
}
 
cout<<"test shared_ptr deleter:"<<endl;
int *p7 = new int(1024);
shared_ptr<int> p8(p7, print_at_delete);
p8 = make_shared<int>(1025);

unique_ptr の基本的な使い方

unique_ptr は、その名前が示すように、指定されたオブジェクトに限定されます。したがって、 unique_ptr のコピーや割り当てなどはできませんが、 release 関数を通じて unique_ptr 間で制御を渡すことはできます。

cout<<"test unique_ptr base usage:"<<endl;
unique_ptr<int> up1(new int(1024));
cout<<"up1: "<<*up1<<endl;
unique_ptr<int> up2(up1.release());
cout<<"up2: "<<*up2<<endl;
//unique_ptr<int> up3(up1); // wrong, unique_ptr can not copy
//up2 = up1; // wrong, unique_ptr can not copy
unique_ptr<int> up4(new int(1025));
up4.reset(up2.release());
cout<<"up4: "<<*up4<<endl;

unique_ptr をパラメータおよび戻り値として使用する

上記のコピーに関する制限には 2 つの特殊なケースがあります。つまり、unique_ptr を関数の戻り値およびパラメータとして使用できるという暗黙的なものもあります。コピーですが、不可能ではありません。

unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
}
 
void process_unique_ptr(unique_ptr<int> up)
{
cout<<"process unique ptr: "<<*up<<endl;
}
 
cout<<"test unique_ptr parameter and return value:"<<endl;
auto up5 = clone(1024);
cout<<"up5: "<<*up5<<endl;
process_unique_ptr(move(up5));
//cout<<"up5 after process: "<<*up5<<endl; // would cause segmentfault

ここでの std::move 関数については後で詳しく説明します^_^

unique_ptr deleter

unique_ptr は、shared_ptr とは異なり、deleter をテンプレートパラメータの型で指定する必要があります。幸いなことに、私たちは強力なツール decltype を持っています。そうでないと、書くのが面倒になってしまいます。

cout<<"test unique_ptr deleter:"<<endl;
int *p9 = new int(1024);
unique_ptr<int, decltype(print_at_delete) *> up6(p9, print_at_delete);
unique_ptr<int> up7(new int(1025));
up6.reset(up7.release());

weak_ptr

weak_ptr は通常、shared_ptr と組み合わせて使用​​されます。これは、shared_ptr が指すオブジェクトを指すことができますが、オブジェクトの参照カウントは増加しません。このように、weak_ptr が指すオブジェクトが実際に解放されている可能性があります。したがって、weak_ptr には、オブジェクトを指すshared_ptr の取得を試みるロック関数があります。

cout<<"test weak_ptr basic usage:"<<endl;
auto p10 = make_shared<int>(1024);
weak_ptr<int> wp1(p10);
cout<<"p10 use_count: "<<p10.use_count()<<endl;
//p10.reset(new int(1025)); // this will cause wp1.lock() return a false obj
shared_ptr<int> p11 = wp1.lock();
if(p11) cout<<"wp1: "<<*p11<<" use count: "<<p11.use_count()<<endl;

概要

shared_ptr は参照カウントを使用して、参照先のオブジェクトを管理します。
shared_ptr は、新しい式によって返されるポインターを使用して初期化できますが、新しい式によって返されるポインターをshared_ptr に割り当てることはできません。
新しい式によって返されたポインタがshared_ptrによって管理されると、通常のポインタを介してこのメ​​モリにアクセスしないでください。
shared_ptr は、reset メソッドを通じて別のオブジェクトを指すようにリセットできます。このとき、元のオブジェクトの参照カウントは 1 つ減ります。
shared_ptr がオブジェクトを解放するときに呼び出される削除関数をカスタマイズできます。
unique_ptr は、指定されたオブジェクトに排他的です。
unique_ptr に値をコピーしたり割り当てたりすることはできませんが、release 関数を通じて unique_ptr 間で制御を転送することはできます。
unique_ptr は関数の戻り値とパラメータとして使用できます。
unique_ptr はデリーターを設定することもでき、デリーターの種類はテンプレートのパラメーターで指定する必要があります。
weak_ptr は通常、shared_ptr と組み合わせて使用​​されます。これは、shared_ptr が指すオブジェクトを指すことができますが、オブジェクトの参照カウントは増加しません。
weak_ptr には、オブジェクトを指すshared_ptr を取得しようとするロック関数があります。

以上がこの記事の全内容です。皆さんの学習に役立つことを願っています。また、皆さんも PHP 中国語 Web サイトをサポートしていただければ幸いです。

C++11 の新機能であるスマート ポインター (shared_ptr/unique_ptr/weak_ptr) に関連する記事をさらに知りたい場合は、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。