1

私は現在 C++ を学んでおり、単純な AddressBook アプリケーションを実装して知識を実践しています。クラスと、人の姓でエントリにアクセスするための STL マップを実装するクラス
から始めました。今、私は次のコードにたどり着きました:EntryAddressBook

Entry AddressBook::get_by_last_name(string last_name){
    if(this->addr_map.count(last_name) != 0){
        //What can I do here?
    } else {
        return addr_map[last_name];
    }

-1, Error Messageスクリプト言語では、関数が失敗したことを示すために (A List in Python) のようなものを返すだけです。アプリケーションロジックの一部であるため、例外をスローしたくありません。呼び出しクラスは、コンソールに何かを出力するか、メッセージ ボックスを開くことによって、要求に応答できる必要があります。ここで、ある種の Invalid State を Class に導入することで、C++ でScripting Language Approach
を実装することを考えました。しかし、それは C++ の悪い習慣ではないでしょうか? 私のクラス全体の設計が適切ではないのでしょうか? 助けていただければ幸いです。私はまだ C++ を学んでいることを覚えておいてください。Entry

4

7 に答える 7

2

コードに関する簡単なメモ:

if(this->addr_map.count(last_name) != 0){
    //What can I do here?

あなたはおそらくそれを別の方法で望んでいました:

if(this->addr_map.count(last_name) == 0){
    //handle error

しかし、あなたの本当の問題はここにあります:

return addr_map[last_name];

ここで注意すべき点は次の 2 つです。

  1. operator[]for マップは 2 つのことを行うことができます: 要素が存在する場合はそれを返します。要素が存在しない場合、pair指定されたキーと値の default で新しい (key,value) を作成しますconstructor。おそらくあなたが望んでいたものではありません。ただし、if前のステートメントが正しい方法であった場合、キーが事前に存在することがわかっているため、後者は決して起こりません。
    1. 前に呼び出すことcount()で、効果的mapに要素を見つけようとします。を呼び出すことで、もう一度検索するようにoperator[]指示しています。mapしたがって、単一の値を取得するために 2 倍の作業を行っています。

これを行うためのより良い (より高速な) 方法には、反復子とfindメソッドが含まれます。

YourMap::iterator it = addr_map.find(last_name); //find the element (once)
if (it == addr_map.end()) //element not found
{
    //handle error
}
return *it.second; //return element

さて、本題に戻ります。last_name見つからない場合はどうすればよいですか?他の回答が指摘したように:

  • 最も簡単な解決策は、ポインターを返すことです (見つからない場合は NULL)。
  • を使用しboost::optionalます。
  • を返すだけですが、ユーザーからYourMap::iteratorを「隠蔽」しようとしているように見えるので、それはおそらく悪い考えです。mapAddressBook
  • throwexception。_ しかし、待ってください。まず、このメソッドの呼び出しが「安全」であることを確認する必要があります (またはexception、適切な場合に処理します)。このチェックには、lastNameExistsを呼び出す前に呼び出す必要があるようなブール メソッドが必要get_by_last_nameです。もちろん、正方形 1 に戻ります。1 つの値を取得するために 2 つの検索操作を実行しています。安全ですが、多くの呼び出しを行っている場合get_by_last_name、これは別のソリューションで最適化するのに適した場所になる可能性があります (さらに、間違いなく、例外はあまり建設的ではありません: そこにないものを検索することの何が問題なのか、は?)。
  • それが現実のものではないが、非常に貧弱なデザインであることを示すためのdummyメンバーを作成します(扱いにくい、直観に反する、無駄が多いなど)。Entry Entry

ご覧のとおり、最初の 2 つのソリューションがはるかに優れています。

于 2013-02-18T12:40:55.453 に答える
1

他の回答はさまざまなアプローチを提供しており、それらのほとんどは有効です。私はまだこれを見ませんでした:

デフォルト値を持つ 2 番目のパラメーターを追加できます。

Entry AddressBook::get_by_last_name(string last_name, const Entry& default_value){
    if(this->addr_map.count(last_name) == 0){
        return default_value; 
    } else {
        return addr_map[last_name];
    }

この特定の例では、存在しない姓に適切なデフォルト値がない可能性がありますが、多くの状況では存在します。

于 2013-02-18T12:47:55.487 に答える
1

関数の戻り値の型に基づいて、例外をスローすることは間違いなく「正しい」C++のことです。

ただし、次のような関数が役立つ場合があります。

bool AddressBook::lastNameExists(const string &last_name)
{
    return addr_map.count(last_name) > 0;
}

現在のコードは「値によって」エントリを返すため、返されたエントリを変更してもマップは更新されないことに注意してください。これが偶然なのか設計によるものなのかはわかりません...

于 2013-02-18T12:30:56.110 に答える
0

姓ごとに複数のエントリを持つことができるという事実は別として。

を削除するgetterと、問題が解決したか、少なくとも別の場所に移動しました。

特定AddressBookの姓を持つ人を表示するように に指示します。何もない場合は何もできません。

AddressBookRenderer renderer;
AddressBook contacts;

contacts.renderSurnames("smith", renderer);
contacts.renderCompletions("sm", renderer);
//etc
于 2013-02-18T12:41:16.583 に答える
0

std::map (および他のコンテナーが行うこと) を行うことができます。

検索関数から反復子を返します。
検索で有用な値が見つからない場合は、反復子を end() に返します。

class AddressBook
{
        typedef  <Your Container Type>  Container;
    public:
        typedef  Container::iterator   iterator;


        iterator get_by_last_name(std::string const& lastName) {return addr_map.find[lastName];}

        iterator end()                                         {return addr_map.end();}
};

アドレス帳はコンテナのようなオブジェクトです。
検索でアイテムが見つからないことはよくありますが、エラー処理コードを組み込むのに十分なコンテキストがありません (アドレス帳は多くの場所から使用でき、場所ごとに異なるエラー処理のアイデアがあるため)。

そのため、not found 状態のテストをアドレス帳から移動する必要があります。
「Python」と同じように、マーカーを返します。C++ では、これは通常 end() への反復子であり、呼び出し元のコードがチェックして適切なアクションを実行できます。

 AddressBook&  ab = getAddressBookRef();
 AddressBook::iterator find = ab.get_by_last_name("cpp_hobbyist");
 if (find != ab.end())
 {
     Entity&  person *find;  // Here you have a reference to your entity.
     // you can now manipulate as you want.
 }
 else
 {
     // Display appropriate error message
 }
于 2013-02-18T15:01:27.477 に答える
0

C++ では、関数で問題が発生したことを通知する方法がいくつかあります。

呼び出し元のコードが無効な値として認識する特別な値を返すことができます。これはNULL、関数がポインターを返す必要がある場合はポインター、関数が配列内のインデックスを返す場合は負の値、またはカスタム クラス (Entryクラスなど) の場合は、特別なEntry::invalid値または類似のものを定義できます。これは、呼び出し関数によって検出できます。

呼び出しコードは次のようになります

if ( entryInstance->get_by_last_name("foobar") != Entry::invalid) 
{
  // here goes the code for the case where the name is valid
} else {
  // here goes the code for the case where the name is invalid
}

一方、C++ 例外メカニズムを使用して、関数に例外をスローさせることができます。このために、独自の例外クラスを作成できます (または から派生した標準ライブラリで定義されたクラスを使用できますstd::exception)。関数はthrow例外になり、呼び出し元のコードはtry...catchステートメントで例外をキャッチする必要があります。

try
  {
    entryInstance->get_by_last_name("foobar")
  }
  catch (Exception e)
  {
    // here goes the code for the case where the name is invalid
  }
  // here goes the code for the case where the name is valid
于 2013-02-18T12:18:41.563 に答える