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++固有のパターンを使用する必要がありますか?それを行うための適切な方法に関する提案をいただければ幸いです。
参照中はNULLにしないでください。基本的に3つのオプションがあり、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;
}
std::pair
をbool
とともに使用して、アイテムが有効かどうかを示します(注: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);
}
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::optional
のstd::pair
の主な利点は、ユニット化されたboost::optional
値がカプセル化する型を構築しないことです。これは、デフォルトコンストラクターを持たない型で機能し、重要なデフォルトコンストラクターを持つ型の時間/メモリを節約することを意味します。
また、マップを2回検索しないように(イテレーターを再利用して)サンプルを変更しました。
なぜ「ポインタを使用する以外に」?ポインターの使用is C++で行う方法。言及したisNull()
関数のような何かを持つ「オプション」タイプを定義しない限り。 (またはboost::optional
などの既存のものを使用します)
参照は、nullにならないように設計され、保証されています。 「だからどうやってそれらをヌルにするのか」という質問は無意味です。 「ヌル可能参照」が必要な場合は、ポインターを使用します。
すべてのタイプに特別なメソッドを実装する場合の問題を回避する、ナイスで比較的非侵入的なアプローチの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
ただし、呼び出し元がポインターの所有権を取得せず、削除しようとしてもいけないことを絶対に明確にする必要があります。
これを処理するいくつかの方法を考えることができます。
boost::optional
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;
}
以下のこのコードは、「無効な」参照を返す方法を示しています。これは、ポインターを使用するための単なる別の方法です(従来の方法)。
他のユーザーが使用するコードでこれを使用することはお勧めしません。参照を返す関数は常に有効な参照を返すことが期待されているためです。
_#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:
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;
};
さらに別のオプション-「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)
}