オブジェクトを安全に破壊する方法。 その他の考え

最近、ある有名なオフィス空席を調べていましたが、問題について考えていました(ところで、それらはすべての空席で同じです)。 そして、最初の質問の(私の意見では)最も興味深い側面についてメモを書くことにしました。 たぶん私は他の人に話を聞きますが、今のところは、デストラクタを仮想化する必要があるかどうかを考えることを提案しますか?



答えはそれほど明確ではありません。カットに誘惑するために、STLの実装では仮想デストラクタはごく少数しか見つかりません。



デストラクタに関する質問に対する完全な答えは何ですか?





あまり最新ではない人のための問題の本質





したがって、間違ったデストラクタを表す、すでに退屈しているすべての人の例を次に示します。



 #include <iostream>

クラスA {
公開:
     A(){std :: cout << "A()" << std :: endl;}
     〜A(){std :: cout << "〜A()" << std :: endl;} // 6
 };

クラスB:パブリックA {
公開:
     B(){std :: cout << "B()" << std :: endl;}
     〜B(){std :: cout << "〜B()" << std :: endl;}
 };

 int main(){
     A * a = new B;  // 16
    削除する;  // 17
     0を返します。
 }


結果は次のとおりです。



 A()
 B()
 〜A()


つまり、16行目のコンストラクターBは正直に両方のコンストラクター(AおよびB)を呼び出し、17行目のデストラクタはクラスAデストラクタのみを呼び出しました。これは渡されたリンクのタイプと完全に一致しています。



すべてが正常に機能しましたが、デストラクタBが呼び出されなかったため、メモリリーク、記述子、およびその他の有用なリソースにつながる可能性がありました。



これに対処する方法は?





一般的な答えは、仮想デストラクタです





6行目にvirtualという単語を追加した場合:



 virtual〜A(){std :: cout << "〜A()" << std :: endl;} // 6


みんな幸せになる



 A()
 B()
 〜B()
 〜A()


ハブで何度も議論されました。 非常に良い記事がありました。 しかし、仮想メソッドがどのように機能するかではなく、より哲学的な質問を議論したいと思います。





仮想メソッドを公開するのは良いことですか?





パブリックメソッドが作成されたユーザーを思い出してみましょう。 それらはクラスのインターフェースを定義し、クラスを使用する人のために作成さます。



そして、 仮想メソッドは何ですか? 正しい-クラスの動作を構成します。 つまり、クラスの機能を拡張するユーザー向けです。



おそらく、すべての正気な人は、クラスの使用とクラスの開発は2つの異なるタスクであると言うでしょう。 それらを混ぜる必要はありません。 そのため、NVI(非仮想インターフェイス)アプローチ、ブリッジなどの動作パターン、および抽象化と実装を分離するその他のトリックが発明されました。 そのような分離の有用性はもはや疑いの余地はありません。 ここでは、すべての詳細とオプションを詳細に説明するのではなく、結論を導きます。



結論:仮想メソッドを公開することはあまり良くありません。



パブリック仮想メソッドが緊急に必要な場合、これはインターフェイスとその実装が緊密に接続されていることを示す信号であり、これは良くなく、あなたの側に来ることができます。



2番目の質問:





ポリモーフィックはオブジェクトを破壊するのに適していますか?





ご存知のように、作成されたものはすべて削除する必要があります。 混乱したり、ミスをしたり、何かを忘れたり、見逃したりしないようにするには、オブジェクト(自動変数、auto_ptrおよび多くの同様のツール)を削除するプロセスを自動化するか、作成したポイントからそれほど遠くない場所にあるオブジェクトを削除する必要があります。



たとえば、特定のコンテナのコンストラクタで(ヒープ上に)オブジェクトを作成する場合、同じコンテナのデストラクタでこのオブジェクトを削除するのが適切です。 この場合、多態性除去の必要がないことに注意してください。



結論:多態的な除去は疑わしいものです。



ポリモーフィックな削除の緊急の必要性を感じる場合-これは、設計について考える機会です。 実際、多態的なデストラクタはそれほど必要ではありません。 おそらく、デザインをわずかに確認し、多態的な削除を放棄し、作成操作と削除操作を互いに近づけて、コードをよりシンプルで一貫性のあるものにする必要があります。 オブジェクトをポリモーフィックな運命にしますが、作成と削除はペアの操作です。 オブジェクトは非常に特定のタイプで作成され、このタイプを忘れずに削除することは論理的です。 これは常に可能とは限りませんが、非常に頻繁に可能です。





さらに、仮想メソッドはすぐにいくつかの制限を作成します。





これについては詳しく説明しませんが、少なくとも1つの仮想メソッドが存在すると、追加のオーバーヘッドが発生し、制限が課されます。 たとえば、そのようなオブジェクトは共用体では使用できません。



結論:最初の仮想メソッドを作成する前に、しばらく考えることができます。本当に必要なのでしょうか?





それでは、理想的な基本クラスのデストラクタは何でしょうか?





All話的に。



デストラクタを非仮想およびパブリックにする (最初の例のように)ことは、MKADでローラースケートに乗ることと同じです。 1つは巧妙な動きではありません。回避する時間はありませんでしたが、今ではビデオは分離されており、あなたは分離されています...



仮想のデストラクタを作成することは、アスファルト舗装のスケートリンクをモスクワリングロードに沿って運転するようなものです。 あなたは危険にさらされていません、あなたは戦車のようです。 あなたの人生を恐れることなく円を切ることができます。 しかし、これは最速の方法ではありません。 さらに、あなたの動きは一般的な流れと矛盾することになります。



デストラクターを仮想でなく安全なものにすることは、エアコン、車輪の後ろのパーソナルドライバー、グラスシャンパンを手にした装甲車でMKADを動き回るようなものです。 それはあなたにとって安全で便利なだけでなく、他の人にとっても不便ではありません。 外国車には注意を払ってくださいが、スケートリンクが通過する場所ならどこでも運転することはできません。



保護された非仮想デストラクタは、ほとんどすべての人に適しています。 これは仮想ではなく、クラスユーザーが最初の例に示したような緊急事態を作成することを許可しません。 基本クラスを直接使用することはできませんが、これもしばしば便利です。 つまり、エラーからあなたを守り、より良いコードを書くことを強制します。



舗装は良いことです。 時には彼らは本当に働く必要があります。 しかし、これが輸送の唯一のモードではなく、さらに、それらを使用する方が便利なことがよくあります。



答え(フル):



小さくて長くないものを書く場合-デストラクタを公開して仮想化し、何も考えないでください。 仮想デストラクタが誰かに失敗するケースはありません。 それは信頼でき、それに関して何も問題はありません。



開発(改善、他のプラットフォームへの移行)の見通しでソフトウェアを何年も作成する場合、保護されたデストラクタについて真剣に考える必要があります。 抽象化と実装の明確な区別は有用です。





すべての幸せな休日と新年の成功!





有名な会社の興味深い質問に感謝します。 彼らがどのような答えを期待していたのか知​​りたいですか? すでに回答用の非常に小さな入力フィールド。 しかし、私は彼らの質問に対する答えの一部だけに触れました。



upd: この記事に対する回答がありました。 おそらく何も答えません。 私はすでにそれと診断されています。 著者は明らかに権限を認めていません。 本は彼にとって議論ではない。 しかし、ノートの建設的な部分では、一般的な継承の危険性について非常に正しい考えがあります。 著者が最後に書いたコーデックファクトリについては、同じ質問のセットから質問4について書くつもりなら、彼はそれについての物語を読むことを望みます。



upd2:なんらかの理由で、この投稿をYandexへの攻撃と見なしている人がいます。 保証します! この投稿には、隠された意味や攻撃はありません。 私は彼らの質問を読んだだけで、議論に値するように思えたので、メモを書きました。 それだけです。



All Articles