在像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
}
但是,具备实现这种为每个类魔术方法似乎沉重。 此外,它似乎并不明显,当对象的内部状态应该从“空”被设置为“不空”。
是否有任何替代这种模式? 我已经知道它可以用指针来完成,但我想知道如何/如果它可以与引用来完成。 或者我应该放弃在返回C“空”的对象++,并使用一些C ++ - 特定的模式? 用正确的方法做的任何建议,将不胜感激。
中引用你不能这样做,因为他们不应该为NULL。 基本上有三种选择,一是使用指针,使用值语义别人。
随着指针(注意:这需要同时调用者有一个指向它的资源没有得到破坏,也确保调用者知道它并不需要删除的对象):
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::optional
过std::pair
是一个未初始化boost::optional
值不构造类型其包封。 这意味着它适用于那些没有默认构造函数的类型,并节省了时间/内存类型有一个不平凡的默认构造函数。
我还修改您的例子所以你不(通过重新使用迭代器),搜索地图的两倍。
为什么“除了使用指针”? 使用指针是你做的在C ++的方式。 除非你确定它有类似的一些“可选”类型isNull()
你提到的功能。 (或使用现有的,像boost::optional
)
参考设计,并保证, 永远不会是空的 。 问:“让我怎么让他们空”是荒谬的。 您可以使用指针,当你需要一个“可空引用”。
一个很好的和相对非侵入性的方法,如果实施为各类特殊的方法避免了问题,是与使用boost.optional 。 它本质上是一个模板的包装,它允许你检查所保存的值是否为“有效”与否。
顺便说一句,我认为这是在文档很好的解释,但要注意boost::optional
的bool
,这是一个建筑,这是很难解释的。
编辑 :问题询问有关“空引用”,但代码段中,通过返回值的函数。 如果确实函数返回一个参考:
const someResource& getSomething(const std::string& name) const ; // and possibly non-const version
那么函数只会使有道理的,如果someResource
到过一辈子至少只要在返回的参考对象的(否则你竟被dhave悬空参考)被提及。 在这种情况下,它似乎完全没有返回一个指针:
const someResource* getSomething(const std::string& name) const; // and possibly non-const version
但你必须绝对清楚的是,来电者不带指针的所有权,不应该试图将其删除。
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)
不像Java和C#在C ++参考对象不能为空。
所以我会建议两种方法我在这种情况下使用。
1 - 代替参考用型具有一个空比如std :: shared_ptr的
2 - 获得参考的外部参数和返回成功布尔。
bool SomeClass::getSomething(std::string name, SomeResource& outParam) {
if (content_.find(name) != content_.end())
{
outParam = content_[name];
return true;
}
return false;
}
这里有几个想法:
选择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;
};
下面这段代码演示了如何返回“无效”的引用; 它是使用指针(常规方法)的只是以不同的方式。
不建议您在将被他人使用的代码使用,因为预期是,返回引用函数总是返回有效的引用。
#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)
返回一个值 ,在这种情况下,通过代表nullptr
-你可以如用0
,到参考的地址设置。 因为,如果你使用指针被这个地址现在可以进行检查。