从返回引用的函数提前返回的最佳方法

Kon*_*rad 8 c++ function pass-by-reference

让我们说我们有一个形式的功能:

const SomeObject& SomeScope::ReturnOurObject()
{
    if( ! SomeCondition )
    {
        // return early
        return  ;
    }

    return ourObject;
}
Run Code Online (Sandbox Code Playgroud)

显然,上面的代码有一个问题,如果条件失败,那么我们就如何从这个函数返回一个问题.我的问题的关键是处理这种情况的最佳方法是什么?

sbi*_*sbi 24

这不是语法问题,而是设计问题.您必须指定ReturnOurObject()SomeConditiontrue 时应返回的内容.这主要取决于该功能将用于什么.你还没有告诉我们.

根据设计问题,我看到了一些可能的语法方法:

  • 返回对其他对象的引用; 你必须在某个地方有一些ersatz对象
  • 有一个特殊的"无对象返回"对象,你返回一个引用; 客户可以检查这个; 如果他们不检查他们得到合理的默认行为
  • 返回一个指针,而不是一个引用; 客户端必须始终检查函数的返回值
  • 抛出异常; 如果SomeCondition是客户无法处理的特殊情况,那将是合适的
  • 断言; 如果SomeCondition应该总是持有,那就应该断言


Adr*_*ore 13

在这种情况下,我会使用指针而不是引用.事实上,这个标准(可选或强制返回值)是我在指针和引用之间的决定.


Ste*_*sop 6

我可能会这样做:

const SomeObject& SomeScope::ReturnOurObject()
{
    if( ! SomeCondition )
    {
        throw SomeException();
    }
    return ourObject;
}

const SomeObject *SomeScope::ReturnOurObjectIfPermitted()
{
    return SomeCondition ? &ourObject : 0;
}
Run Code Online (Sandbox Code Playgroud)

也许还有:

bool SomeScope::CheckMode();
    return SomeCondition;
}
Run Code Online (Sandbox Code Playgroud)

然后呼叫者有一些选择:

// 1 - "I know that I'm in the right mode"
myScope.ReturnOurObject().DoSomething();

// 2 - "I don't know whether I'm in the right mode, but I can cope either way"
if (SomeObject *myObject = myScope.ReturnOurObjectIfPermitted()) {
    myObject->DoSomething();
} else {
    DoSomethingElse();
}

// 2 - alternate version:
if (myScope.CheckMode()) {
    SomeObject &myObject = myScope.ReturnOurObject();
    myObject.DoSomething();
} else {
    DoSomethingElse();
}

// 3 - "I don't know whether I'm in the right mode. If I'm not then
// I can't deal with it now, but some higher-level code can"
try {
    // ... several calls deep ...
    myScope.ReturnOurObject().DoSomething();
    // ... several returns back ...
} catch (SomeException &e) {
    DoSomethingElse();
}
Run Code Online (Sandbox Code Playgroud)

您根据前提条件设计一个函数的界面,即确定它可以完成其工作的责任.

如果是来电者的责任,那么他们需要有能力确保这一点.你可能想要检查,只是为了安全起见.当不满足所谓的"前置条件"时抛出异常会使它们更像是建议而不是条件,并且如果您不介意检查的运行时开销,它可以很好地工作.我通常对前置条件相当不宽容,所以实际上我可能会用断言替换条件异常,并记录不能在错误模式下调用该函数.这取决于调用者对模式的控制程度 - 显然,如果它随意改变(例如,如果"SomeCondition"是"UART上没有可用的字节")那么你需要一个非常不同的接口,如果它只是改变了当客户端代码调用某个函数来改变它时.

如果调用者没有责任让模式正确,那么您的界面不应该写出您的实现无法兑现的检查.在这种情况下,如果"预期"没有对象返回,则返回不应该是引用.除非(a)你可以发出一个特殊的"抱歉,它没有用"对象返回,调用者可以检查,或者(b)你很乐意在"预期"的情况下抛出异常.IMO(b)在C++中很少见.(a)通常不比返回空指针好,但偶尔也是如此.例如,返回一个空集合意味着"没有什么你可以在这里看到的"可能会导致一些非常干净的调用代码,如果DoSomethingElse()是"什么也不做".因此,除了期望的职责外,界面还取决于预期的用例.


Yac*_*oby 5

如果您不想将返回类型更改为指针,则可以使用null对象模式


Tim*_*sch 5

'ourObject'的生命周期是什么?它在哪里创建?

如果您需要提前返回而不触及/更新我们的对象,并且它存在于您返回的位置,我看不到返回对它的引用的问题.

如果你正在尝试通知存在错误,你可能不得不抛出异常而不是提前返回,因为你的函数同意的合同说它会返回对'SomeObject'的引用.

如果您要返回对临时的引用,则最好解决问题...


Pat*_*ola 5

我将返回一个布尔值并将对象引用作为参数传递给函数:

bool getObject(SomeObject& object)
{
    if( condition )
         return false;

    object = ourObject;
    return true;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您现在只有在函数返回true时才填充对象.

  • 但是,这与原始代码不同.原始代码返回对ourObject的引用(因此,通过非const引用,将来可以通过调用者的引用看到对ourObject的修改).这是myObject的副本,就像调用函数时一样.两者都不是对或错,但它们有不同的目的. (5认同)