検索結果が見つからない場合は「NULL」オブジェクトを返します


94

私はC ++にかなり慣れていないので、学んでいる間、多くのJava主義で設計する傾向があります。とにかく、Javaでは、特定のパラメーターに一致するTからオブジェクトを返す 'search'メソッドを含むクラスがあるCollection< T >場合、そのオブジェクトを返しnullます。オブジェクトがコレクション内に見つからなかった場合は、を返します。次に、私の呼び出し関数でチェックするだけですif(tResult != null) { ... }

C ++では、nullオブジェクトが存在しないと値を返せないことがわかりました。オブジェクトが見つからなかったことを呼び出し元の関数に通知するタイプTの「インジケーター」を返したいだけです。例外的な状況ではないので、例外を投げたくありません。

これは私のコードが今どのように見えるかです:

class Node {
    Attr& getAttribute(const string& attribute_name) const {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return NULL; // what should this be?
    }

private:
    vector<Attr> attributes;
}

どのように変更すれば、そのようなマーカーを付けることができますか?


6
例外とNULLが常に唯一の解決策であるとは限りません。多くの場合、返される値を選択して、見つからないことを示しstd::find(first, last, value)ますlast。たとえば、一致する要素がない場合に返されます。
Cascabel

回答:


70

C ++では、参照をnullにすることはできません。何も見つからなかった場合にオプションでnullを返したい場合は、参照ではなくポインタを返す必要があります。

Attr *getAttribute(const string& attribute_name) const {
   //search collection
   //if found at i
        return &attributes[i];
   //if not found
        return nullptr;
}

それ以外の場合、参照による戻りを主張する場合、属性が見つからない場合は例外をスローする必要があります。

(ちなみに、私はあなたのメソッドがconstconst属性を返すのを少し心配しています。哲学的な理由から、私は返すことをお勧めしconst Attr *ます。この属性を変更したい場合は、非constメソッドでオーバーロードできます非const属性も返します。)


2
ありがとう。ちなみに、これはそのようなルーチンを設計するための受け入れられた方法ですか?
10

6
@aduric:はい。参照は、結果が存在する必要があることを意味します。ポインターは、結果が存在しない可能性があることを意味します。
ビル・

7
好奇心旺盛ですが、今はc ++ 11のnullptr代わりに戻りNULLますか?
スペクトラル

1
はい、C ++ 11以降では常にnullptrをNULLよりも使用します。以前のバージョンとの下位互換性が必要な場合は、使用しないでください
コンラッドジョーンズ

56

ここにはいくつかの可能な答えがあります。存在する可能性のあるものを返したい。ここにいくつかのオプションがあります。私の最も好ましいものから最も好ましいものまでの範囲です。

  • 参照によって戻り、例外によって信号を見つけることができません。

    Attr& getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            throw no_such_attribute_error;
    }

属性を見つけられないことは、実行の通常の部分であり、したがって、それほど例外的ではない可能性があります。これの処理は騒々しいでしょう。null参照を持つことは未定義の動作であるため、null値を返すことはできません。

  • ポインタで戻る

    Attr* getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return &attributes[i];
       //if not found
            return nullptr;
    }

getAttributeからの結果が非NULLポインタになるかどうかを確認するのを忘れることは簡単であり、バグの簡単な原因です。

  • Boost.Optionalを使用する

    boost::optional<Attr&> getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return boost::optional<Attr&>();
    }

boost :: optionalは、ここで何が行われているのかを正確に示し、そのような属性が見つかったかどうかを検査する簡単な方法を備えています。


補足:std :: optionalは最近C ++ 17に投票されたので、これは近い将来「標準」になるでしょう。


+1最初にboost :: optionalについて言及し、他の代替案については簡単に言及します。
Nemanja Trifunovic 2010

Ya:boost :: optionalがどこかで言及されているのを見ましたが、オーバーヘッドが多すぎると思っていました。このような問題に対して、それを使用することが最善のアプローチである場合は、使用を開始します。
aduric

boost::optionalオーバーヘッドがあまりない(動的割り当てがない)ため、非常に優れています。ポリモーフィック値で使用するには、参照またはポインタをラップする必要があります。
Matthieu M.

2
@MatthieuM。オーバーヘッドのaduricが言及していたのはパフォーマンスではなく、プロジェクトに外部ライブラリを含めるためのコストでした。
スーガン

私の回答の補遺:おそらくC ++ 17の可能性があるため、オプションをstdコンポーネントとして標準化する動きがあることに注意してください。したがって、この手法について知っておく価値はあります。
Kaz Dragon

22

NULLの戻り値を表す静的オブジェクトを簡単に作成できます。

class Attr;
extern Attr AttrNull;

class Node { 
.... 

Attr& getAttribute(const string& attribute_name) const { 
   //search collection 
   //if found at i 
        return attributes[i]; 
   //if not found 
        return AttrNull; 
} 

bool IsNull(const Attr& test) const {
    return &test == &AttrNull;
}

 private: 
   vector<Attr> attributes; 
};

そして、ソースファイルのどこかに:

static Attr AttrNull;

NodeNullはAttr型であってはなりませんか?
aduric


2

Java(またはC#)で行った方法では実行できないことを理解したように。これは別の提案です。オブジェクトの参照を引数として渡し、ブール値を返すことができます。コレクションで結果が見つかった場合は、渡される参照に割り当てて「true」を返すか、そうでない場合は「false」を返すことができます。このコードを検討してください。

typedef std::map<string, Operator> OPERATORS_MAP;

bool OperatorList::tryGetOperator(string token, Operator& op)
{
    bool val = false;

    OPERATORS_MAP::iterator it = m_operators.find(token);
    if (it != m_operators.end())
    {
        op = it->second;
        val = true;
    }
    return val;
}

上記の関数は、キー「トークン」に対して演算子を見つけなければなりません。trueを返すものを見つけ、その値をパラメータOperator&opに割り当てます。

このルーチンの呼び出し元コードは次のようになります

Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
    //Do something here if true is returned.
}

1

ここでNULLを返すことができない理由は、戻り値の型をとして宣言したためですAttr&。末尾&は戻り値を「参照」にします。これは基本的に、既存のオブジェクトへのnullになることが保証されていないポインタです。nullを返すことができるようにするには、に変更Attr&Attr*ます。


0

NULL関数の戻り値の型がオブジェクトreferenceではなく、オブジェクトであるため、戻ることができませんpointer


-3

あなたはこれを試すことができます:

return &Type();

6
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
NathanOliver

これはおそらくメソッドスタック上のオブジェクトへのデッドリファレンスを返しますね?
mpromonet
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.