特にスマート ポインターを使用するのが適しているのはいつですか? また、C++ のポインターの使用が推奨されていないのに、なぜスマート ポインターを使用するのでしょうか? 例として:
class Object { }
smart_pointer < Object > pInstance;
//wherein I can use the very simple way
Object instance;
特にスマート ポインターを使用するのが適しているのはいつですか? また、C++ のポインターの使用が推奨されていないのに、なぜスマート ポインターを使用するのでしょうか? 例として:
class Object { }
smart_pointer < Object > pInstance;
//wherein I can use the very simple way
Object instance;
オブジェクトの所有権を維持する必要がある場合は、スマート ポインターが適しています。それらを使用すると、適切な破壊が保証されます。ポインターが参照として扱われる場合、スマート ポインターを使用すると、パフォーマンスが低下することがあります (たとえば、パフォーマンスの点で)。
ポインターは C++ の重要な部分ですが、ムーブ セマンティクスが導入された (本質的に可能になった) C++11 のスマートなポインターのように、ポインターの使用がはるかに簡単になりましたunique_ptr
。そのため、最新のコードでは、使用可能な場合は常にstd::unique_ptr
orを使用する必要std::shared_ptr
があります。
編集:ポインターを使用すると有益な例を求めました。私が考えることができる最も一般的な問題は、一部のシステムのオプション コンポーネントです。このコンポーネントは大量のメモリを使用するため、常に割り当てたり、その割り当てを制御したりする必要はありません (そのため、それ自体を「空」状態にすることはできません。つまり、null 可能ではありません)。Boost.Optional と C++14 っぽいstd::optional
ものは T の POD っぽいサイズのメモリを割り当てるので、そうはなりません。ポインターを使用すると、必要に応じてそのメモリを割り当てることができます。
class Outer {
std::unique_ptr<BigInner> optionalComponent;
public:
void initializeOptionalComponent() {
optionalComponent = new BigInner();
}
void useOptionalComponent() {
if (!optionalComponent)
// handle the error
// operate
}
};
これで問題は解決しますが、明らかな別の問題が発生します。optionalComponent は null になる可能性があり、それを使用するすべての関数が常に有効な状態をチェックする必要があります。単純な値渡しのメンバーであった場合、常に有効な状態になります (または、少なくともそうあるべきです)。そのため、ポインターが必要ない場合は、まったく使用しないvector<MyClass>
でください。プレーンメンバーを使用してください。
とにかく、この場合にスマート ポインターを使用すると、ゼロのルールを維持できます。デストラクタ、コピー コンストラクタ、または代入演算子を記述する必要はなく、クラスは安全に動作します。
簡単な答え : スマート ポインターは (特に) に役立ちます
RAII の適用
ポインターに付随する問題の 1 つと、プログラムのクラッシュ (多くの点で明らかな、ねじれた) の原因の 1 つは、ポインターの下にあるメモリに責任があることです。つまり、( を介してnew
) メモリを動的に割り当てる場合、このメモリを担当し、 を呼び出すことを忘れてはなりませんdelete
。つまりそうなってしまうということで、さらに悪いことに、忘れていなくてもdelete文にたどり着けない場合があります。このコードを検討してください:
void function(){
MyClass* var = new MyClass;
//Do something
delete var;
}
ここで、delete ステートメントに到達する前にこの関数が例外をスローすると、ポインターは削除されません... メモリ リーク !
RAII はこれを回避する方法です。
void function(){
std::shared_ptr<MyClass> var(new MyClass);
//Do something
//No need to delete anything
}
ポインタはオブジェクトによって保持され、そのデストラクタで削除されます。前のコードとの違いは、関数が例外をスローした場合、共有ポインターのデストラクタが呼び出され、ポインターが削除され、メモリ リークが回避されることです。
RAII は、ローカル変数がスコープ外になると、その dtor が呼び出されるという事実を利用しています。
ポインターの所有権の管理
前の例で使用したスマート ポインターに注意してください。はstd::shared_ptr
、ポインターを渡すときに役立つスマート ポインターです。コードの多くの部分で同じオブジェクトへのポインタが必要な場合、削除する場所を決定するのが難しい場合があります。どこかでポインタを削除したいかもしれませんが、コードの別の部分がそれを使用している場合はどうでしょうか? その結果、削除されたポインターにアクセスすることになりますが、これはまったく望ましくありません。std::shared_ptr
これを防ぐのに役立ちます。shared_ptr を渡すと、コードのどの部分が参照されているかが追跡されます。ポインタへの参照がなくなると、このポインタはメモリを削除します。言い換えれば、誰もポインターを使用しなくなった場合、ポインターは安全に削除されます。
std::unique_ptr
その下にあるポインターの唯一の所有者であるポインターを提供するなど、他の問題に対処する他の種類のスマートポインターがあります。
注 - ポリモーフィズムに関する簡単な説明
ポリモーフィズムを使用するにはポインタが必要です。抽象クラスがある場合MyAbstract
(つまり、少なくとも 1 つの仮想クラスがあることを意味しますdoVirtual()
)、インスタンス化することはできません。次のコード:
MyAbstract var;
Can't instantiate abstract class
コンパイルされない場合、コンパイラからの行に沿って何かが得られます。
しかし、これは合法です( とImplA
からImplB
公に継承しMyAbstract
ます:
MyAbstract* varA = new ImplA;
MyAbstract* varB = new ImplB;
varA->doVirtual(); //Will call ImplA implementation
varB->doVirtual(); //Will call ImplB implementation
delete varA;
delete varB;