4

私のコードは次のことをしました:

  1. を使用してaから値を取得しmapますoperator[]
  2. 戻り値を確認し、NULL使用する場合insertはマップに新しい要素を挿入します。

魔法のように、価値のある要素が0マップに表示されました。

数時間のデバッグの後、私は次のことを発見しmapましoperator[] た。キーが見つからない場合は新しい要素を挿入しinsert 、キーが存在する場合は値を変更しません。

マップ値タイプのデフォルトのコンストラクターが存在しない場合でも、コードはコンパイルしてoperator[]挿入し0ます。

これが私を傷つけるのを防ぐことができた方法はありますか(たとえば、これから従うことができるいくつかのコーディング規約)?

4

8 に答える 8

7

明らかなのは、それらが実際にインデックス演算子のセマンティクスであることを学ぶことだと思います。そのため、コンテナー内の要素の存在をテストするために使用しないでください。

代わりに、を使用してfind()ください。

于 2012-05-21T12:06:04.170 に答える
7

これが私を傷つけるのを防ぐことができた方法(たとえば、これから従うことができるコーディング規則)はありますか?

これは皮肉に聞こえるかもしれませんが、ドキュメントを読んでください。

あなたが行ったことは、マップのある程度予想された動作であるため、それを防ぐためにできることはあまりありません。

将来的に注意できることの1つは、次のことです。2番目のステップで、何か間違ったことをしました:

戻り値を確認し、NULL の場合は insert を使用して新しい要素をマップに挿入します。

これは、C++ 標準ライブラリ関数 (C 互換関数および 以外) では機能しませnew。標準ライブラリはポインター、特にヌル ポインターを処理しないため、NULL(または0またはnullptr) に対するチェックはほとんど意味がありません。(それとは別に、マップoperator []が最初にポインターを返すことは意味がありません。明らかに、要素の型(または、それへの参照) を返します)。

実際、標準ライブラリは主に反復子end()を使用するため、使用する場合は、コンテナーの と比較して反復子の有効性を確認してください。

NULL残念ながら、コンパイルされたコード ( に対してチェック)NULLは、実際には C++ で等しいマクロである0ため、整数と比較できます。

C++11nullptrは、個別の型を持つキーワードを導入することでより安全になるため、整数と比較してもコンパイルされません。したがって、これは便利なコーディング規則です。 を使用NULLせず、代わりに C++11 サポートを有効にしてコンパイルし、 を使用しますnullptr

于 2012-05-21T12:10:35.607 に答える
4

実際、 を呼び出したときにoperator []、そのキーの値が見つからない場合、値で初期化されたデフォルト値が挿入されます。

これが発生したくない場合は、次を確認する必要がありますfind

if ( mymap.find(myKey) == mymap.end() )
{
    //the key doesn't exist in a map
}

によって返される値は、operator []それNULLがポインターへのマップである場合にのみになります (または、値を初期化して 0 を生成する型ですが、かなり具体的でしたNULL)。

于 2012-05-21T12:05:15.437 に答える
4

マップ値型のデフォルトのコンストラクターが存在しない場合でも、コードはコンパイルされます

これは絶対に間違っています。operator[]デフォルトのコンストラクターが存在しない場合、コンパイルに失敗するはずです。それ以外は、実装側のエラーです。

あなたのコードは一度だけ使用されているはずinsertです。

于 2012-05-21T12:06:31.433 に答える
2

std::map の最新の実装には、値の存在をチェックし、キーが見つからない場合は例外.at(const Key& key)を返すメンバー関数もあります。std::out_of_range

http://en.cppreference.com/w/cpp/container/map/at

于 2012-05-21T12:09:43.017 に答える
0

[] 演算子を使用する代わりに、マップで find を呼び出します。一致が見つからない場合、エントリは挿入されません。見つかった項目への反復子を返します。

于 2012-05-21T12:05:45.787 に答える
0

ここでは、マップ ルックアップと挿入のユース ケースの例をいくつか示します。既存のアイテムのケースを処理したり、古い値が何であったかを確認したりする場合があります。

class Foo
{
    // Use a typedef so we can conveniently declare iterators
    // and conveniently construct insert pairs
    typedef map<int, std::string> IdMap;
    IdMap id_map;

    // A function that looks up a value without adding anything
    void one(int id)
    {
        IdMap::iterator i = id_map.find(id);

        // See if an entry already exists
        if (i == id_map.end())
            return; // value does not exist

        // Pass the string value that was stored in the map to baz
        baz(i->second);
    }

    // A function that updates an existing value, but only if it already exists
    bool two(int id, const std::string &data)
    {
        IdMap::iterator i = id_map.find(id);

        if (i == id_map.end())
            return false;

        i->second = data;
        return true;
    }

    // A function that inserts a value only if it does NOT already exist
    // Returns true if the insertion happened, returns false if no effect
    bool three(int id, const std::string &data)
    {
        return id_map.insert(IdMap::value_type(id, data)).second;
    }

    // A function that tries to insert if key doesn't already exist,
    // but if it does already exist, needs to get the current value
    void four(int id, const std::string &data)
    {
        std::pair<IdMap::iterator,bool> i =
            id_map.insert(IdMap::value_type(id, data));

        // Insertion worked, don't need to process old value
        if (i->second)
            return true;

        // Pass the id to some imaginary function that needs
        // to know id and wants to see the old string and new string
        report_conflict(id, i->first->second, data);
    }
};

プログラマーは、怠惰または無知から、 への複数回の冗長な呼び出しoperator[]、または への呼び出しの後に へfindの冗長な呼び出しoperator[]、またはへの呼び出しの後に への冗長な呼び出しを行うことがよくあります。マップのセマンティクスを理解していれば、マップを効率的に使用するのは非常に簡単です。findinsert

于 2013-05-28T02:21:24.970 に答える