从指针升级到引用——如何处理 NULL 到引用
Upgrade from pointers to references - How to deal with NULL to reference
我是 C++11 的新手,我 class 是这样的:
class Pair; // defined somewhere...
class IReadOnlyList{
public:
virtual const Pair *get(const char *key) const = 0;
inline const Pair *operator[](const char *key) const{
return get(key);
};
inline bool exists(const char *key) const{
return get(key) != NULL;
};
};
它工作正常,但我想删除指针。但是,如果我改变
Pair *get()
到 Pair &get
,那么我无法处理不存在的对。
选项是-
1. 例外
2. 空对象。因为Pair是POD(标准布局),我完全可以用这个
3. return 封装 Pair * 的中间对象 - 听起来很蠢
4. 保留 NULL :)
有没有我遗漏的选项?
尽可能避免指针。
在这种情况下,函数 可能 对 return 没有有意义的值,那么 boost::optional
可能是一种解决方案:
virtual boost::optional<Pair> get(const char *key) const = 0;
Return 当您对 return 没有任何有意义的值时 boost::optional<Pair>
的空实例。调用者需要检查 returned boost::optional<Pair>
是否为空或持有实例 Pair
.
此外,在这种情况下,find
似乎比 get
更好——因为函数可能会或可能不会找到与关联的 key
相关的有意义的值。
//return empty optional when no value found
virtual boost::optional<Pair> find(const char *key) const = 0;
如果你想保留 get
作为函数名,那么抛出异常是更好的解决方案:
//throw exception when no value found
virtual const Pair const& get(const char *key) const = 0;
您可以在代码中同时使用这两个函数 — get
可以根据 find
.
来实现
希望对您有所帮助。
我认为期望是要走的路。由于某些原因,人们低估了他们的需求。
如果你无论如何都不能抛出异常,你可以将结果作为引用传递,并且 return bool 来指示函数结果——如果它是真的——作为引用传递的对象是有效的结果,如果为 false- 处理失败并且不使用该对象。
bool get(const char *key, Pair& pair) const{
if (good){
pair = goodObject;
return true;
}
return false;
}
但同样,使用例外。
你能 return 按值对吗?因此,值将在堆栈上或任何地方创建,您不必担心指针泄漏
之类的。
// Interface
Pair get (const char* key) const = 0;
...
// Usage
Pair result = irp.get();
result.doSomething();
因此,您的 get 看起来像:
Pair IRPointerClass::get (const char* key)
{
Pair result;
... initialize result using key...
return result;
}
还有另一种方法——使用空对象。
在不知道 Pair class 的内部数据成员的情况下,无法准确指出 Pair 的哪个内部数据成员将用于此目的,但在一个项目中我们使用了这种声明 public 静态常量对象的方法(例如Pair) 表示错误或无效的情况。
调用者可以检查该对象的某些内部数据成员(或调用某些安全方法 returning bool)以确定该对象是否合法。
它不像异常那样优雅,也不像 NULL 指针那样干净,但您始终可以 return 一个有效的对象引用,并且也消除了调用者是否处理了异常的恐惧。
由于您return引用(指针)而不是(!)值:
例外情况:
不好,如果不存在是有效(非异常)return 值。
空对象:
正如你提到的,一个笨拙的解决方法。
Return 中间对象,封装了结果:
将是一种方法(参见 boost::optional),但您 return 一个参考(指针)
保留 nullptr:
在这种情况下简单而聪明。不测试 null undefined 的 return 值(不遵守合同)
我认为没有比 4 更聪明的了。)
除了保留指针和做一些愚蠢的事情,这里有我能想到的所有带有中间容器的选项class。
为了能够编译,我创建了虚拟 Pair class 和函数而不是 get() 方法。
注意:我没有尝试在这里处理内存泄漏 - 请不要post评论...
#include <stdio.h>
// This is Pair class
class Pair{
public:
void print() const{
printf("Hello\n");
}
};
// This is Pair Container class,
// will be interesting to be rewritten with templates
class _Pair{
public:
_Pair(const Pair *p = NULL) : _pair(p){};
inline const Pair & val() const{
return *_pair;
}
inline explicit operator bool() const {
return _pair != NULL;
}
inline const Pair & operator *() const {
return *_pair;
}
inline const Pair & operator ()() const {
return *_pair;
}
inline const Pair * operator ->() const {
return _pair;
}
private:
const Pair *_pair;
};
// this is GET() method
const _Pair & getPair(bool a){
static const _Pair pnull = _Pair();
if (a){
return *new _Pair( new Pair() );
}
return pnull;
}
// this is usage
int main(int argc, char** argv)
{
const _Pair & p = getPair(false);
if (p){
p().print();
}
const _Pair & q = getPair(true);
if (q){
// option 1: nice, but additional ()
// same as boost::optional
(*q).print();
// option 2: too long
const Pair & pp = *q;
pp.print();
// option 3: no difference from pointer
q->print();
// option 4: using method - looks not bad
// same as boost::optional
q.val().print();
// option 5: nive and yummy :)
q().print();
}
return 0;
}
我确实检查了 boost::optional,然后我检查了 std::optional 的(未来)实验规范,并得出以下结论:
https://github.com/nmmmnu/HM3/blob/master/std/std_container.h
和
https://github.com/nmmmnu/HM3/blob/master/std/std_optional.h
然后因为我会经常用到它,在定义Pair的地方,我也定义了:
class OPair : public std_optional<const Pair>{
public:
OPair(const Pair *pair = nullptr) : std_optional(pair){};
};
然后 IList::get() 看起来像这样:
const OPair IArray::get(const char *key) const override{
uint64_t index;
if (lookup(key, & index))
return nullptr; // LINE 1
return _data[index]; // LINE 2
}
LINE 1 为NULL,已自动转换为Pair *,然后转换为OPair。
LINE 2 正在返回 Pair *,已自动转换为 OPair。
使用 IList 看起来像:
OPair p = list.get("bla");
if (p){
printf("%s\n", p->getVal());
//or
printf("%s\n", p().getVal());
//or
printf("%s\n", p.val().getVal());
}
我是 C++11 的新手,我 class 是这样的:
class Pair; // defined somewhere...
class IReadOnlyList{
public:
virtual const Pair *get(const char *key) const = 0;
inline const Pair *operator[](const char *key) const{
return get(key);
};
inline bool exists(const char *key) const{
return get(key) != NULL;
};
};
它工作正常,但我想删除指针。但是,如果我改变
Pair *get()
到 Pair &get
,那么我无法处理不存在的对。
选项是-
1. 例外
2. 空对象。因为Pair是POD(标准布局),我完全可以用这个
3. return 封装 Pair * 的中间对象 - 听起来很蠢
4. 保留 NULL :)
有没有我遗漏的选项?
尽可能避免指针。
在这种情况下,函数 可能 对 return 没有有意义的值,那么 boost::optional
可能是一种解决方案:
virtual boost::optional<Pair> get(const char *key) const = 0;
Return 当您对 return 没有任何有意义的值时 boost::optional<Pair>
的空实例。调用者需要检查 returned boost::optional<Pair>
是否为空或持有实例 Pair
.
此外,在这种情况下,find
似乎比 get
更好——因为函数可能会或可能不会找到与关联的 key
相关的有意义的值。
//return empty optional when no value found
virtual boost::optional<Pair> find(const char *key) const = 0;
如果你想保留 get
作为函数名,那么抛出异常是更好的解决方案:
//throw exception when no value found
virtual const Pair const& get(const char *key) const = 0;
您可以在代码中同时使用这两个函数 — get
可以根据 find
.
希望对您有所帮助。
我认为期望是要走的路。由于某些原因,人们低估了他们的需求。
如果你无论如何都不能抛出异常,你可以将结果作为引用传递,并且 return bool 来指示函数结果——如果它是真的——作为引用传递的对象是有效的结果,如果为 false- 处理失败并且不使用该对象。
bool get(const char *key, Pair& pair) const{
if (good){
pair = goodObject;
return true;
}
return false;
}
但同样,使用例外。
你能 return 按值对吗?因此,值将在堆栈上或任何地方创建,您不必担心指针泄漏 之类的。
// Interface
Pair get (const char* key) const = 0;
...
// Usage
Pair result = irp.get();
result.doSomething();
因此,您的 get 看起来像:
Pair IRPointerClass::get (const char* key)
{
Pair result;
... initialize result using key...
return result;
}
还有另一种方法——使用空对象。 在不知道 Pair class 的内部数据成员的情况下,无法准确指出 Pair 的哪个内部数据成员将用于此目的,但在一个项目中我们使用了这种声明 public 静态常量对象的方法(例如Pair) 表示错误或无效的情况。 调用者可以检查该对象的某些内部数据成员(或调用某些安全方法 returning bool)以确定该对象是否合法。 它不像异常那样优雅,也不像 NULL 指针那样干净,但您始终可以 return 一个有效的对象引用,并且也消除了调用者是否处理了异常的恐惧。
由于您return引用(指针)而不是(!)值:
例外情况: 不好,如果不存在是有效(非异常)return 值。
空对象: 正如你提到的,一个笨拙的解决方法。
Return 中间对象,封装了结果: 将是一种方法(参见 boost::optional),但您 return 一个参考(指针)
保留 nullptr: 在这种情况下简单而聪明。不测试 null undefined 的 return 值(不遵守合同)
我认为没有比 4 更聪明的了。)
除了保留指针和做一些愚蠢的事情,这里有我能想到的所有带有中间容器的选项class。
为了能够编译,我创建了虚拟 Pair class 和函数而不是 get() 方法。
注意:我没有尝试在这里处理内存泄漏 - 请不要post评论...
#include <stdio.h>
// This is Pair class
class Pair{
public:
void print() const{
printf("Hello\n");
}
};
// This is Pair Container class,
// will be interesting to be rewritten with templates
class _Pair{
public:
_Pair(const Pair *p = NULL) : _pair(p){};
inline const Pair & val() const{
return *_pair;
}
inline explicit operator bool() const {
return _pair != NULL;
}
inline const Pair & operator *() const {
return *_pair;
}
inline const Pair & operator ()() const {
return *_pair;
}
inline const Pair * operator ->() const {
return _pair;
}
private:
const Pair *_pair;
};
// this is GET() method
const _Pair & getPair(bool a){
static const _Pair pnull = _Pair();
if (a){
return *new _Pair( new Pair() );
}
return pnull;
}
// this is usage
int main(int argc, char** argv)
{
const _Pair & p = getPair(false);
if (p){
p().print();
}
const _Pair & q = getPair(true);
if (q){
// option 1: nice, but additional ()
// same as boost::optional
(*q).print();
// option 2: too long
const Pair & pp = *q;
pp.print();
// option 3: no difference from pointer
q->print();
// option 4: using method - looks not bad
// same as boost::optional
q.val().print();
// option 5: nive and yummy :)
q().print();
}
return 0;
}
我确实检查了 boost::optional,然后我检查了 std::optional 的(未来)实验规范,并得出以下结论:
https://github.com/nmmmnu/HM3/blob/master/std/std_container.h
和
https://github.com/nmmmnu/HM3/blob/master/std/std_optional.h
然后因为我会经常用到它,在定义Pair的地方,我也定义了:
class OPair : public std_optional<const Pair>{
public:
OPair(const Pair *pair = nullptr) : std_optional(pair){};
};
然后 IList::get() 看起来像这样:
const OPair IArray::get(const char *key) const override{
uint64_t index;
if (lookup(key, & index))
return nullptr; // LINE 1
return _data[index]; // LINE 2
}
LINE 1 为NULL,已自动转换为Pair *,然后转换为OPair。 LINE 2 正在返回 Pair *,已自动转换为 OPair。
使用 IList 看起来像:
OPair p = list.get("bla");
if (p){
printf("%s\n", p->getVal());
//or
printf("%s\n", p().getVal());
//or
printf("%s\n", p.val().getVal());
}