web-dev-qa-db-ja.com

C ++で「NULL参照」を返しますか?

JavaScriptやPHPなどの動的に型付けされた言語では、次のような機能をよく実行します。

_function getSomething(name) {
    if (content_[name]) return content_[name];
    return null; // doesn't exist
}
_

存在する場合はオブジェクトを返し、存在しない場合はnullを返します。

参照を使用するC++で同等のものは何ですか?一般的に推奨されるパターンはありますか?この目的のためにisNull()メソッドを持つフレームワークを見ました:

_SomeResource SomeClass::getSomething(std::string name) {
    if (content_.find(name) != content_.end()) return content_[name];
    SomeResource output; // Create a "null" resource
    return output;
}
_

次に、呼び出し元はその方法でリソースをチェックします。

_SomeResource r = obj.getSomething("something");
if (!r.isNull()) {
    // OK
} else {
    // NOT OK
}
_

ただし、この種の魔法のメソッドを各クラスに実装する必要があると思われます。また、オブジェクトの内部状態を「null」から「not null」に設定する必要がある場合も明らかではありません。

このパターンに代わるものはありますか?ポインターを使用して実行できることは既に知っていますが、参照を使用してどのように/実行できるのか疑問に思っています。または、C++で「null」オブジェクトを返すのをgiveめて、C++固有のパターンを使用する必要がありますか?それを行うための適切な方法に関する提案をいただければ幸いです。

40
laurent

参照中はNULLにしないでください。基本的に3つのオプションがあり、1つはポインターを使用し、もう1つは値セマンティクスを使用します。

  1. ポインターを使用する(注:これには、呼び出し側がポインターを持っている間、リソースが破壊されないことが必要です。また、呼び出し側がオブジェクトを削除する必要がないことを確認してください):

    SomeResource* SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return &(*it);  
        return NULL;  
    }
    
  2. std::pairboolとともに使用して、アイテムが有効かどうかを示します(注:SomeResourceに適切なデフォルトコンストラクターが必要であり、構築に費用がかからないことが必要です)。

    std::pair<SomeResource, bool> SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return std::make_pair(*it, true);  
        return std::make_pair(SomeResource(), false);  
    }
    
  3. boost::optional を使用:

    boost::optional<SomeResource> SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return *it;  
        return boost::optional<SomeResource>();  
    }
    

値のセマンティクスが必要で、Boostを使用できる場合は、オプション3をお勧めします。 boost::optionalstd::pairの主な利点は、ユニット化されたboost::optional値がカプセル化する型を構築しないことです。これは、デフォルトコンストラクターを持たない型で機能し、重要なデフォルトコンストラクターを持つ型の時間/メモリを節約することを意味します。

また、マップを2回検索しないように(イテレーターを再利用して)サンプルを変更しました。

44
Sven

なぜ「ポインタを使用する以外に」?ポインターの使用is C++で行う方法。言及したisNull()関数のような何かを持つ「オプション」タイプを定義しない限り。 (またはboost::optionalなどの既存のものを使用します)

参照は、nullにならないように設計され、保証されています。 「だからどうやってそれらをヌルにするのか」という質問は無意味です。 「ヌル可能参照」が必要な場合は、ポインターを使用します。

26
jalf

すべてのタイプに特別なメソッドを実装する場合の問題を回避する、ナイスで比較的非侵入的なアプローチの1つは、 boost.optional で使用されるアプローチです。保持されている値が「有効」かどうかを確認できるテンプレートラッパーです。

ところで、これはドキュメントで十分に説明されていると思いますが、boost::optional of bool、これは解釈が難しい構造です。

Edit:質問は「NULL参照」について尋ねますが、コードスニペットには値で返す関数があります。その関数が実際に参照を返した場合:

const someResource& getSomething(const std::string& name) const ; // and possibly non-const version

参照されるsomeResourceが少なくとも参照を返すオブジェクトの寿命と同じ寿命を持っている場合にのみ関数は意味があります(そうでない場合は、ぶら下がり参照があります)。この場合、ポインターを返すことはまったく問題ありません。

const someResource* getSomething(const std::string& name) const; // and possibly non-const version

ただし、呼び出し元がポインターの所有権を取得せず、削除しようとしてもいけないことを絶対に明確にする必要があります

6
juanchopanza

これを処理するいくつかの方法を考えることができます。

  • 他の人が示唆したように、boost::optional
  • オブジェクトが無効であることを示す状態にします(Yuk!)
  • 参照の代わりにポインターを使用する
  • nullオブジェクトであるクラスの特別なインスタンスを持っている
  • 失敗を示す例外をスローします(常に適用されるわけではありません)
5
vhallac

Javaとは異なり、C++参照オブジェクトのC#はnullにできません。
したがって、この場合に使用する2つの方法をアドバイスします。

1-参照の代わりに、std :: shared_ptrなどのnullを持つ型を使用します

2-参照を出力パラメーターとして取得し、成功した場合はブール値を返します。

bool SomeClass::getSomething(std::string name, SomeResource& outParam) {
    if (content_.find(name) != content_.end()) 
    {
        outParam = content_[name];
        return true;
    }
    return false;
}
2
Roee Gavirel

以下のこのコードは、「無効な」参照を返す方法を示しています。これは、ポインターを使用するための単なる別の方法です(従来の方法)。

他のユーザーが使用するコードでこれを使用することはお勧めしません。参照を返す関数は常に有効な参照を返すことが期待されているためです。

_#include <iostream>
#include <cstddef>

#define Nothing(Type) *(Type*)nullptr
//#define Nothing(Type) *(Type*)0

struct A { int i; };
struct B
{
    A a[5];
    B() { for (int i=0;i<5;i++) a[i].i=i+1; }
    A& GetA(int n)
    {
        if ((n>=0)&&(n<5)) return a[n];
        else return Nothing(A);
    }
};

int main()
{
    B b;
    for (int i=3;i<7;i++)
    {
        A &ra=b.GetA(i);
        if (!&ra) std::cout << i << ": ra=nothing\n";
        else std::cout << i << ": ra=" << ra.i << "\n";
    }
    return 0;
}
_

マクロNothing(Type)は、valueを返します。この場合、nullptrで表されます-参照のアドレスである_0_も使用できますセット。このアドレスは、ポインタを使用しているかのように確認できます。

1
slashmais

ここにいくつかのアイデアがあります:

代替案1:

class Nullable
{
private:
    bool m_bIsNull;

protected:
    Nullable(bool bIsNull) : m_bIsNull(bIsNull) {}
    void setNull(bool bIsNull) { m_bIsNull = bIsNull; }

public:
    bool isNull();
};

class SomeResource : public Nullable
{
public:
    SomeResource() : Nullable(true) {}
    SomeResource(...) : Nullable(false) { ... }

    ...
};

代替案2:

template<class T>
struct Nullable<T>
{
    Nullable(const T& value_) : value(value_), isNull(false) {}
    Nullable() : isNull(true) {}

    T value;
    bool isNull;
};
1
Andrew Tomazos

さらに別のオプション-「null」オブジェクトが本当に返されることを望まず、代わりに「empty/invalid」オブジェクトが必要な場合に時々使用するオプション:

// List of things
std::vector<some_struct> list_of_things;
// An emtpy / invalid instance of some_struct
some_struct empty_struct{"invalid"};

const some_struct &get_thing(int index)
{
    // If the index is valid then return the ref to the item index'ed
    if (index <= list_of_things.size())
    {
        return list_of_things[index];
    }

    // Index is out of range, return a reference to the invalid/empty instance
    return empty_struct; // doesn't exist
}

それは非常にシンプルで、(あなたが反対側で何をしているのかに応じて)反対側でnullポインタチェックを行う必要を回避できます。たとえば、もののリストを生成している場合:

for (const auto &sub_item : get_thing(2).sub_list())
{
    // If the returned item from get_thing is the empty one then the sub list will
    // be empty - no need to bother with nullptr checks etc... (in this case)
}
0
code_fodder