したがって、私はC++で英語のトライデータ構造の実装を作成しようとしています。 Trie
およびTrieNode
クラスを作成しました。 TrieNode
クラスは、そのコンストラクターでvector<string>
を受け取ります。これは、トライを構成する単語のリストです。
このvector<string>
を取得するための私の解決策は、EnglishWordsListGenerator
クラスを使用することでした。このクラスは、コンストラクターで、おそらく有効な英語の単語のリストを含むファイルのファイル名の文字列を受け取ります。 (これまで人気のあったenable1.txt
ファイル)。
私はC++を初めて使用するので、オブジェクトを初期化するときに、コンストラクターから直接ファイルを読み取るのが良い方法と見なされるかどうかはわかりません。結局のところ、操作が失敗した場合はどうなりますか?一方、BjarneがRAIIを強く要求していることは知っています。ここで正しいことは何ですか?
コンストラクタのシグネチャを持つクラスを見るとき
EnglishWordsListGenerator(const std::string &wordFileName)
このコンストラクターが指定されたファイルを読み取ることは明らかであるため(そして時間が必要です)、呼び出し元がこれからの可能な例外に注意する必要があることを理解するのは難しくありません(ファイルIOは失敗する可能性があります。そのため、他の回答が言っていることにもかかわらず、このデザインは大丈夫ですです。
誤解しないでください。将来的には、この設計ではもはや十分ではない要件が得られる可能性がありますが、この単純なインターフェースと動作がプログラムのすべての要件である限り、KISSとYAGNIの原則。「念のため」に別個の初期化メソッドを提供することで、複雑化を防ぎます。
C++を初めて使用する場合は、コンストラクターで大きな作業を行うことを避けた方がよい場合があります。コンストラクターは言語の動作に密接に関係しており、コンストラクターが予期せず呼び出されることを本当に望まない(explicit
はあなたの友達です!)。コンストラクタも値を返すことができないという点でかなりユニークです。これにより、エラー処理がより複雑になる可能性があります。
この種の推奨事項は、C++や、コンストラクターが呼び出される原因となる可能性のあるすべてのことについて学ぶほど重要ではなくなります。完璧な世界では、正しい答えは、ライブラリはユーザーが常に望んでいたことを正確に実行する必要があるということです。多くの場合、C++では、コンストラクター内で物事を行うことによって実装するのが最適です。ただし、コンストラクターを十分に理解して、ユーザーを隅に追い込まないようにする必要があります。ユーザーがコンストラクターを呼び出すつもりがなかったために呼び出されたためにプログラムが正常に動作しない理由のデバッグは、骨の折れる作業です。
同様に、例外が発生するスコープは、変数を使用する場所ではなく、変数が作成されるスコープと絡むため、コンストラクターでの例外処理はより困難になる可能性があります。例外が発生すると、例外が激怒する可能性がありますbeforemain()
が実行され、ユーザーの例外を処理する機能が拒否されます。これは、グローバル変数を使用していて、エラーがわかりにくい場合に発生する可能性があります。
両極端の中間に位置する1つのオプションは、ファクトリーメソッドを使用することです。ユーザーがファクトリメソッドを使用することを期待している場合、その時点で少し余分な初期化を行うことができます。ファクトリーメソッドは、コンストラクターのように誤って呼び出されることはないため、混乱させることは困難です。
コンストラクターで作業を行うことの結果を理解していると確信したら、それは強力なツールになります。コンストラクタで作業を行う最終的な例は、_lock_guard
_クラスです。ロックガードは、ミューテックスなどのロックを渡すことによって構築されます。彼らはコンストラクター(実際の作業を行う)中に.lock()
を呼び出し、デストラクター中に.unlock()
を呼び出します。
これらのクラスは、そのように使用するように設計されているため、コンストラクターで実際の作業を行う必要はありません。 _lock_guard
_を使用する人は誰でも、コンストラクタが機能することをよく知っています。
_{
lock_guard guardMe(myLock); // locks myLock
doSomeStuff();
doMoreStuff();
// as I leave this block, guardMe will unlock myLock. It will do so
// even if I leave via an exception!
}
_
_lock_guard
_クラスの実装を見ると、それらの注意の80%以上が、驚きを防ぐためにコンストラクターを正しく管理する方法に焦点を当てていることが容易にわかります。適切に実装されていれば、非常に強力です。不適切に実装された_lock_guards
_は、実際には別のことをするように見えるため、存在の悩みの種になる可能性があります。
コンストラクターでファイルからオブジェクトをロードすることはお勧めできません。
他の回答の優れたC++引数を超えて、クリーンなコード原則を追加します:関数は1つのことだけを行う必要があります。わかりました。コンストラクタは通常の関数ではありませんが、原則は適用されます。
私は以下を提案します:
Trie
をできる限り一般的にします。それを使用できる他の多くのアプリケーションがあります。string
またはistream
からフィードするなど、アプリケーションごとに異なるアプローチを使用できます。結局のところ、アプリの将来のバージョンでは、異なる言語のデータベースから単語を取得する可能性があります。いくつかの理由で、コンストラクターでファイルを読み取ることはないと思います。
他の理由があると私は確信しています。それらは私の頭に浮かんだ最初の理由にすぎません。ファイルが小さく、一度しか読み取っていない場合は、おそらく問題ありません。