2週間前、ISO C ++標準の委員会がジャクソンビルで会合しました。 今日は短い要約を紹介し、ジャクソンビル会議でなされた革命的な決定について書きたいと思います。 詳細については、ポインターのないC ++の記事を読むことをお勧めします 。 言語標準化委員会は、ポインターがC ++ 20で非推奨になり、C ++ 23から削除される可能性が最も高いと判断しました。
率直に言って、革命的と思われるのは、長い進化の最終段階に過ぎません。
C ++でのポインターの進化
ポインターは最初からC ++に存在します。 Cから取得しました。C++の開発の最初から、大きな損失を伴わずにポインター管理をより安全にする傾向が常にありました。
C ++ 98では、排他的所有権を表すためにstd::auto_ptr
を取得しました。 しかし、 std::auto_ptr
は大きな欠陥がありました。 std::auto_ptr
をコピーすると、リソースの所有権がコピーに転送されます。 コピーは動くように見えました。 以下の画像は、 std::auto_ptr
不快な動作を示しています。
それは非常に悪く、多くの深刻なバグにつながりました。 そのため、C ++ 11でstd::unique_ptr
を取得し、C ++ 11で非推奨のstd::auto_ptr
を宣言し、C ++ 17から完全に削除しました。 さらに、所有権を制御するために、C ++ 11でstd::shared_ptr
およびstd::weak_ptr
を取得しました。 コピーすることはできませんが、 std::unique_ptr
移動できます。また、 std::shared_ptr
をコピーまたは割り当てると、参照ポインターカウントが増加します。 ここを見てください:
C ++ 11以降、C ++にはマルチスレッドライブラリがあります。 定義によりstd::shared_ptr
共有されていますが、スレッドセーフではないため、これによりstd::shared_ptr
管理が非常に複雑になります。 カウンタを備えた制御部分のみがスレッドセーフですが、監視対象リソースのアドレスにはアクセスできません。 これは、カウンターの変更がアトミック操作であることを意味しますが、リソースが一度だけ削除されるという保証はありません。 このため、C ++ 20のアトミックスマートポインターを取得します: std::atomic_shared_ptr
およびstd::atmic_weak_ptr
標準化委員会の提案の詳細については、「 Atomic smart pointers」を参照してください。
次に、将来のC ++ 20およびC ++ 23標準のより興味深い部分に移りましょう。 ポインターはC ++ 20で非推奨になり、C ++ 23から削除されます。 次の3つの単語を考えてみましょう。NoNew New(NNN)。
std :: unique_ptrは私たちを救います
しかし、待ってください。C++の教義はどうですか。不要なものにお金を払わないでください。 ポインターなしでどのようにプログラムできますか? std::unique_ptr
使用してください。 その設計から、 std::unique_ptr
は、通常のポインターと同じくらい高速で経済的であり、自動リソース管理という明確な利点があります。
以下は、簡単なパフォーマンステストです。
// all.cpp #include <chrono> #include <iostream> static const long long numInt= 100000000; int main(){ auto start = std::chrono::system_clock::now(); for ( long long i=0 ; i < numInt; ++i){ int* tmp(new int(i)); delete tmp; // std::shared_ptr<int> tmp(new int(i)); // std::shared_ptr<int> tmp(std::make_shared<int>(i)); // std::unique_ptr<int> tmp(new int(i)); // std::unique_ptr<int> tmp(std::make_unique<int>(i)); } std::chrono::duration<double> dur= std::chrono::system_clock::now() - start; std::cout << "time native: " << dur.count() << " seconds" << std::endl; }
このプログラムは、1億int
メモリを割り当てて解放しint
。 ポインターstd::shared_ptr
およびstd::unique_ptr
を2つのバリエーションで使用します。 LinuxおよびWindowsで最大限の最適化の有無にかかわらずプログラムをコンパイルしています。 次の数値が取得されます。
LinuxおよびWindowsのstd::unique_ptr
2つのバリエーションは、通常のポインターと同じパフォーマンスを示します。 このテストの詳細については、前回の記事「 メモリ消費とスマートポインタパフォーマンス」を参照してください。
所有権のセマンティクス
正直なところ、私たちはポインターを使用しています。特に、通常のポインターを頻繁に使用しています。 ポインターを使用する必要があるかどうかの問題は、次のようになります。所有者は誰ですか? 幸いなことに、コードの助けを借りて、これを明確に表現できます。
- ローカルオブジェクト 。 所有者としてのRantime C ++は、そのようなリソースの寿命を自動的に制御します。 同じことがグローバルオブジェクトまたはクラスメンバーにも当てはまります。 ディレクトリはこれをスコープに減らします。
- リンク :私は所有者ではありません。 リソースが空にならないようにするだけです。
- 通常のポインター :私は所有者ではありません。 リソースがある場合は、そのリソースのみを参照します。 リソースを削除しないでください。
- std :: unique_ptr :私はリソースの排他的所有者です。 リソースを明示的に解放できます。
- std :: shared_ptr :他の
std::shared_ptr
とリソースを共有します。 他の人が必要としない場合、共有リソースを明示的に削除できます。 - std :: weak_ptr :私はリソースの所有者ではありませんが、
std::weak_ptr::lock
メソッドを呼び出すときに一時的にリソースを分割できます。
ポインターを使用するための6つのプラクティスのうちの1つを変更するだけで済み、C ++の開発における次のステップに満足しています。