在C++中返回"NULL引用"?

lau*_*ent 41 c++ methods null design-patterns object

在动态类型语言(如JavaScript或PHP)中,我经常执行以下功能:

function getSomething(name) {
    if (content_[name]) return content_[name];
    return null; // doesn't exist
}
Run Code Online (Sandbox Code Playgroud)

我返回一个对象,如果它存在或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;
}
Run Code Online (Sandbox Code Playgroud)

然后调用者将检查资源:

SomeResource r = obj.getSomething("something");
if (!r.isNull()) {
    // OK
} else {
    // NOT OK
}
Run Code Online (Sandbox Code Playgroud)

然而,必须为每个类实现这种魔术方法似乎很重.当对象的内部状态应该从"null"设置为"not null"时,似乎并不明显.

这种模式还有其他选择吗?我已经知道它可以使用指针完成,但我想知道如何/如果它可以用引用完成.或者我应该放弃在C++中返回"null"对象并使用一些特定于C++的模式?任何有关正确方法的建议都将受到赞赏.

Sve*_*ven 45

您不能在引用期间执行此操作,因为它们永远不应为NULL.基本上有三个选项,一个使用指针,另一个使用值语义.

  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;  
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用std::paira 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);  
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用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>();  
    }
    
    Run Code Online (Sandbox Code Playgroud)

如果你想要价值语义并且能够使用Boost,我建议选择三.boost::optional过度的主要优点std::pair是,单元化boost::optional值不构造其封装类型.这意味着它适用于没有默认构造函数的类型,并为具有非平凡构造函数的类型节省时间/内存.

我还修改了你的例子,因此你没有两次搜索地图(通过重用迭代器).

  • @Troubadour:实际上很难推出可以容纳任何类型的"可选"; 你需要为该类型嵌入一个具有正确大小和对齐的POD对象,这根本不是直截了当的.(当然,如果将其限制为默认可构造类型,则更容易,在这种情况下,它与选项2基本相同). (2认同)

jal*_*alf 26

为什么"除了使用指针"?使用指针你在C++中的方式.除非你定义一些"可选"类型,它类似于isNull()你提到的功能.(或使用现有的,像boost::optional)

参考资料的设计和保证永远不会为空.问"所以如何让它们为空"是荒谬的.当您需要"可空引用"时,可以使用指针.

  • @jalf:欺骗编译器创建NULL引用并不是很难(`int&n =*static_cast <int*>(NULL);`).但是,我绝不会认为这是一个好主意.:) (2认同)

jua*_*nza 6

如果为所有类型实现特殊方法,那么一个很好且相对非侵入性的方法可以避免这个问题,它与boost.optional一起使用.它本质上是一个模板包装器,允许您检查保持的值是否"有效".

顺便说一句,我认为这是在文档很好的解释,但要注意boost::optionalbool,这是一个建筑,这是很难解释的.

编辑:问题询问"NULL引用",但代码片段有一个按值返回的函数.如果该函数确实返回了一个引用:

const someResource& getSomething(const std::string& name) const ; // and possibly non-const version
Run Code Online (Sandbox Code Playgroud)

那么这个函数只有在someResource被引用的生命周期至少与返回引用的对象的生命周期一样长时才有意义(否则你将会有一个悬空引用).在这种情况下,返回指针似乎完全没问题:

const someResource* getSomething(const std::string& name) const; // and possibly non-const version
Run Code Online (Sandbox Code Playgroud)

但是你必须绝对清楚地表明调用者没有获得指针的所有权,不应该尝试删除它.


vha*_*lac 5

我可以想到几种方法来解决这个问题:

  • 正如其他人所说,使用 boost::optional
  • 使对象具有表明它无效的状态(Yuk!)
  • 使用指针而不是引用
  • 有一个特殊的类实例,即null对象
  • 抛出异常表示失败(并不总是适用)

  • 与downvote一起进行的解释会很好.特别是对于有多个建议的东西.:) (6认同)