如果没有找到搜索结果,返回一个“ NULL”对象

我是 C + + 的新手,所以在学习的时候,我倾向于使用大量的 Java 语言进行设计。无论如何,在 Java 中,如果我有一个带有“ search”方法的类,它将从 Collection< T >返回一个匹配特定参数的对象 T,我将返回该对象,如果在集合中没有找到该对象,我将返回 null。然后在我的调用函数中检查 if(tResult != null) { ... }

在 C + + 中,我发现如果对象不存在,就不能返回 null值。我只想返回一个 T 类型的“指示器”,它通知调用函数没有找到对象。我不想抛出一个异常,因为它不是真正的特殊情况。

这就是我的代码现在的样子:

class Node {
Attr& getAttribute(const string& attribute_name) const {
//search collection
//if found at i
return attributes[i];
//if not found
return NULL; // what should this be?
}


private:
vector<Attr> attributes;
}

我怎样才能改变它,这样我就可以给那种记号笔?

108998 次浏览

If you want a NULL return value you need to use pointers instead of references.

References can't themselves be NULL.

(Note to the future comment posters: Yes you can have the address of a reference be NULL if you really really try to).

See my answer here for a list of differences between references and pointers.

In C++, references can't be null. If you want to optionally return null if nothing is found, you need to return a pointer, not a reference:

Attr *getAttribute(const string& attribute_name) const {
//search collection
//if found at i
return &attributes[i];
//if not found
return nullptr;
}

Otherwise, if you insist on returning by reference, then you should throw an exception if the attribute isn't found.

(By the way, I'm a little worried about your method being const and returning a non-const attribute. For philosophical reasons, I'd suggest returning const Attr *. If you also may want to modify this attribute, you can overload with a non-const method returning a non-const attribute as well.)

You are unable to return NULL because the return type of the function is an object reference and not a pointer.

The reason that you can't return NULL here is because you've declared your return type as Attr&. The trailing & makes the return value a "reference", which is basically a guaranteed-not-to-be-null pointer to an existing object. If you want to be able to return null, change Attr& to Attr*.

There are several possible answers here. You want to return something that might exist. Here are some options, ranging from my least preferred to most preferred:

  • Return by reference, and signal can-not-find by exception.

    Attr& getAttribute(const string& attribute_name) const
    {
    //search collection
    //if found at i
    return attributes[i];
    //if not found
    throw no_such_attribute_error;
    }

It's likely that not finding attributes is a normal part of execution, and hence not very exceptional. The handling for this would be noisy. A null value cannot be returned because it's undefined behaviour to have null references.

  • Return by pointer

    Attr* getAttribute(const string& attribute_name) const
    {
    //search collection
    //if found at i
    return &attributes[i];
    //if not found
    return nullptr;
    }

It's easy to forget to check whether a result from getAttribute would be a non-NULL pointer, and is an easy source of bugs.

  • Use Boost.Optional

    boost::optional<Attr&> getAttribute(const string& attribute_name) const
    {
    //search collection
    //if found at i
    return attributes[i];
    //if not found
    return boost::optional<Attr&>();
    }

A boost::optional signifies exactly what is going on here, and has easy methods for inspecting whether such an attribute was found.


Side note: std::optional was recently voted into C++17, so this will be a "standard" thing in the near future.

You can easily create a static object that represents a NULL return.

class Attr;
extern Attr AttrNull;


class Node {
....


Attr& getAttribute(const string& attribute_name) const {
//search collection
//if found at i
return attributes[i];
//if not found
return AttrNull;
}


bool IsNull(const Attr& test) const {
return &test == &AttrNull;
}


private:
vector<Attr> attributes;
};

And somewhere in a source file:

static Attr AttrNull;

You can try this:

return &Type();

As you have figured out that you cannot do it the way you have done in Java (or C#). Here is another suggestion, you could pass in the reference of the object as an argument and return bool value. If the result is found in your collection, you could assign it to the reference being passed and return ‘true’, otherwise return ‘false’. Please consider this code.

typedef std::map<string, Operator> OPERATORS_MAP;


bool OperatorList::tryGetOperator(string token, Operator& op)
{
bool val = false;


OPERATORS_MAP::iterator it = m_operators.find(token);
if (it != m_operators.end())
{
op = it->second;
val = true;
}
return val;
}

The function above has to find the Operator against the key 'token', if it finds the one it returns true and assign the value to parameter Operator& op.

The caller code for this routine looks like this

Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
//Do something here if true is returned.
}

There is one more option that could be considered in this situation - depending on your design. You can return the value using an argument to your function and make the function return bool, e.g.

bool getAttribute(const string& attribute_name, Attr& returnAttr) const {
//search collection
//if found at i
returnAttr = attributes[i];
return true;
//if not found
return false;
}