在 C++ 中返回“NULL 引用”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10371094/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Returning a "NULL reference" in C++?
提问by laurent
In dynamically typed languages like JavaScript or PHP, I often do functions such as:
在 JavaScript 或 PHP 等动态类型语言中,我经常执行以下功能:
function getSomething(name) {
if (content_[name]) return content_[name];
return null; // doesn't exist
}
I return an object if it exists or null
if not.
如果存在或null
不存在,我将返回一个对象。
What would be the equivalent in C++ using references? Is there any recommended pattern in general? I saw some frameworks having an isNull()
method for this purpose:
在 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;
}
Then the caller would check the resource that way:
然后调用者会以这种方式检查资源:
SomeResource r = obj.getSomething("something");
if (!r.isNull()) {
// OK
} else {
// NOT OK
}
However, having to implement this kind of magic method for each class seems heavy. Also it doesn't seem obvious when the internal state of the object should be set from "null" to "not null".
但是,必须为每个类实现这种神奇的方法似乎很繁重。此外,当对象的内部状态应该从“null”设置为“not null”时,这似乎并不明显。
Is there any alternative to this pattern? I already know it can be done using pointers, but I am wondering how/if it can be done with references. Or should I give up on returning "null" objects in C++ and use some C++-specific pattern? Any suggestion on the proper way to do that would be appreciated.
这种模式有什么替代方案吗?我已经知道它可以使用指针来完成,但我想知道如何/是否可以使用引用来完成。或者我应该放弃在 C++ 中返回“空”对象并使用一些 C++ 特定的模式?任何有关正确方法的建议将不胜感激。
回答by Sven
You cannot do this during references, as they should never be NULL. There are basically three options, one using a pointer, the others using value semantics.
您不能在引用期间执行此操作,因为它们永远不应为 NULL。基本上有三种选择,一种使用指针,另一种使用值语义。
With a pointer (note: this requires that the resource doesn't get destructed while the caller has a pointer to it; also make sure the caller knows it doesn't need to delete the object):
SomeResource* SomeClass::getSomething(std::string name) { std::map<std::string, SomeResource>::iterator it = content_.find(name); if (it != content_.end()) return &(*it); return NULL; }
Using
std::pair
with abool
to indicate if the item is valid or not (note: requires that SomeResource has an appropriate default constructor and is not expensive to construct):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); }
Using
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>(); }
使用指针(注意:这要求资源在调用者有指向它的指针时不会被破坏;还要确保调用者知道它不需要删除对象):
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
与 abool
一起使用来指示该项目是否有效(注意:要求 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<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>(); }
If you want value semantics and have the ability to use Boost, I'd recommend option three. The primary advantage of boost::optional
over std::pair
is that an unitialized boost::optional
value doesn't construct the type its encapsulating. This means it works for types that have no default constructor and saves time/memory for types with a non-trivial default constructor.
如果你想要值语义并且有能力使用 Boost,我会推荐选项三。boost::optional
over的主要优点std::pair
是未初始化的boost::optional
值不会构造其封装的类型。这意味着它适用于没有默认构造函数的类型,并为具有非平凡默认构造函数的类型节省时间/内存。
I also modified your example so you're not searching the map twice (by reusing the iterator).
我还修改了您的示例,因此您不会两次搜索地图(通过重用迭代器)。
回答by jalf
Why "besides using pointers"? Using pointers isthe way you do it in C++. Unless you define some "optional" type which has something like the isNull()
function you mentioned. (or use an existing one, like boost::optional
)
为什么“除了使用指针”?使用指针是您在 C++ 中的做法。除非您定义了一些“可选”类型,它具有isNull()
您提到的功能。(或使用现有的,如boost::optional
)
References are designed, and guaranteed, to never be null. Asking "so how do I make them null" is nonsensical. You use pointers when you need a "nullable reference".
引用被设计并保证永远不会为 null。问“那么我如何使它们为空”是荒谬的。当您需要“可为空引用”时使用指针。
回答by juanchopanza
One nice and relatively non-intrusive approach, which avoids the problem if implementing special methods for all types, is that used with boost.optional. It is essentially a template wrapper which allows you to check whether the value held is "valid" or not.
一种很好且相对非侵入性的方法,它在为所有类型实现特殊方法时避免了问题,是与boost.optional一起使用的方法。它本质上是一个模板包装器,它允许您检查所持有的值是否“有效”。
BTW I think this is well explained in the docs, but beware of boost::optional
of bool
, this is a construction which is hard to interpret.
顺便说一句,我认为这是在文档很好的解释,但要注意boost::optional
的bool
,这是一个建筑,这是很难解释的。
Edit: The question asks about "NULL reference", but the code snippet has a function that returns by value. If that function indeed returned a reference:
编辑:问题询问“NULL 引用”,但代码片段有一个按值返回的函数。如果该函数确实返回了一个引用:
const someResource& getSomething(const std::string& name) const ; // and possibly non-const version
then the function would only make sense if the someResource
being referred to had a lifetime at least as long as that of the object returning the reference (otherwise you woul dhave a dangling reference). In this case, it seems perfectly fine to return a pointer:
那么该函数只有在someResource
被引用的生命周期至少与返回引用的对象的生命周期一样长时才有意义(否则你将有一个悬空引用)。在这种情况下,返回一个指针似乎完全没问题:
const someResource* getSomething(const std::string& name) const; // and possibly non-const version
but you have to make it absolutely clearthat the caller does not take ownership of the pointer and should not attempt to delete it.
但是您必须明确表示调用者不拥有指针的所有权,也不应该尝试删除它。
回答by vhallac
I can think of a few ways to handle this:
我可以想到几种方法来处理这个问题:
- As others suggested, use
boost::optional
- Make the object have a state that indicates it is not valid (Yuk!)
- Use pointer instead of reference
- Have a special instance of the class that is the null object
- Throw an exception to indicate failure (not always applicable)
- 正如其他人建议的那样,使用
boost::optional
- 使对象具有表明它无效的状态(Yuk!)
- 使用指针代替引用
- 有一个类的特殊实例,它是空对象
- 抛出异常以指示失败(并非总是适用)
回答by Roee Gavirel
unlike Java and C# in C++ reference object can't be null.
so I would advice 2 methods I use in this case.
与 Java 和 C# 不同,C++ 中的引用对象不能为 null。
所以我会建议我在这种情况下使用的两种方法。
1 - instead of reference use a type which have a null such as std::shared_ptr
1 - 而不是引用使用具有空值的类型,例如 std::shared_ptr
2 - get the reference as a out-parameter and return Boolean for success.
2 - 将引用作为输出参数获取并返回布尔值以表示成功。
bool SomeClass::getSomething(std::string name, SomeResource& outParam) {
if (content_.find(name) != content_.end())
{
outParam = content_[name];
return true;
}
return false;
}
回答by Andrew Tomazos
Here are a couple of ideas:
这里有几个想法:
Alternative 1:
备选方案 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) { ... }
...
};
Alternative 2:
备选方案 2:
template<class T>
struct Nullable<T>
{
Nullable(const T& value_) : value(value_), isNull(false) {}
Nullable() : isNull(true) {}
T value;
bool isNull;
};
回答by slashmais
This code below demonstrates how to return "invalid" references; it is just a different way of using pointers (the conventional method).
下面的代码演示了如何返回“无效”引用;它只是使用指针的不同方式(传统方法)。
Not recommended that you use this in code that will be used by others, since the expectation is that functions that return references always return valid references.
不建议您在其他人将使用的代码中使用它,因为期望返回引用的函数始终返回有效引用。
#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;
}
The macro Nothing(Type)
returns a value, in this case that represented by nullptr
- you can as well use 0
, to which the reference's address is set. This address can now be checked as-if you have been using pointers.
宏Nothing(Type)
返回一个值,在这种情况下,由nullptr
-表示,您也可以使用0
,引用的地址设置到该值。现在可以检查该地址,就像您一直在使用指针一样。
回答by code_fodder
Yet another option - one that I have used from time to time for when you don't really want a "null" object returned but instead an "empty/invalid" object will do:
另一个选项 - 我不时使用的一个选项,当您真的不希望返回“空”对象而是“空/无效”对象时:
// 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
}
Its quite simple and (depending on what you are doing with it at the other end) can avoid the need to do null pointer checks on the other side. For example if you are generating some lists of thing, e.g:
它非常简单(取决于您在另一端使用它做什么)可以避免在另一端进行空指针检查的需要。例如,如果您正在生成一些事物列表,例如:
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)
}