在某些条件下无法返回有意义值的c ++函数

时间:2012-10-26 14:13:38

标签: c++ function pointers null

我有一个成员函数,对象类型为返回值类型:

MyObject myfunction(parameters) {
    if (some condition) { 
        return MyObject(parameters);
    } else { 
        ... no valid object can be created ... 
    } 
}

在某些条件下(在函数体中检查),无法创建并返回MyObject类型的对象。

Beeing只是偶尔的c ++程序员我可以自发地提出三种解决方案:

  1. 如果无法创建有效对象,则将返回值类型更改为* MyObject并返回nullptr(C ++ 11),然后在调用代码中检查是否与nullptr相等。
  2. 如果无法创建任何对象并在调用代码中捕获该对象,则抛出异常。
  3. 使用我定义为无效的某些值创建一个对象,并在使用返回的对象之前检查它。
  4. 处理这种情况的标准方法是什么,以及性能方面的最佳解决方案? ...或者我看不到的一些明显的解决方法......

    最先进的C ++ 11解决方案将是完美的: - )

    到目前为止我的想法:
    解决方案1似乎没问题,但只是C ++ 11,我必须在堆上创建返回的对象,以便能够将它传递给主程序(将对象本身返回给调用函数,从而保持它对于小物体,堆栈可能更快?) 解决方案2可能较慢并导致主程序中的详细编码 解决方案3可能是最慢的(对象是徒劳地创建的),并且在主程序中检查不太方便。

    对于我的代码,没有有效的返回对象是默认情况而不是异常,并且创建的对象相当小,但考虑到不同情况的一般考虑对于其他读者的应用程序肯定是有用的......

    非常感谢大家的帮助: - )

4 个答案:

答案 0 :(得分:7)

通常情况下,返回Boost.Optional有效:

boost::optional<MyObject> myfunction(parameters) {
    if (some condition) { 
        return MyObject(parameters);
    } else { 
        return boost::none;
    } 
}

在通话现场:

auto ret = myfunction(...);
if(ret)
  // use '*ret'  or 'ret.get()'

但正如R. Martinho所提到的,这个解决方案存在缺陷(即,仅移动类型不起作用,因为Boost.Optional尚未更新以支持移动语义)。

答案 1 :(得分:4)

既然你的问题是一般性的,我也会回答一般性问题。

如果你有一个功能,它的工作是创建和返回一个对象,那么 就是它的工作。

现在,如果你想以这样的方式设计这个函数,以便当构建对象所需的certian条件满足然后不返回对象时,你实际上已经改变了这个函数的语义。现在只有一个责任,它有三个:

  1. 确定是否存在构建对象的正确条件
  2. 如果是,请构造并返回对象
  3. 如果不是,则不返回任何内容或某些表示未创建的条件值
  4. Single Responsibility Principle”表明,一般来说,良好的设计要求一个功能(或类或你有什么)应该有一个工作要做。这里,你的功能有三个。

    我建议你的建议方法一般都不是最好的。相反,我会选择:

      

    4:实施单独的功能以确定是否符合资格   构造对象。如果该函数返回true,则调用   构造对象的myFunction&amp;退货。

答案 2 :(得分:4)

根据具体情况,您提出的所有3种解决方案都是有效且通用的。

如果无法创建对象是一个错误情况,可能导致调用函数必须中止,备份和重试,或采取其他严厉措施,然后抛出异常。

如果无法创建对象是一个例程事件,并且您希望调用者检查对象是否已创建并在任何一种情况下都能正常进行,则返回null是一个很好的解决方案。

如果有一个合理的虚拟或空白对象可以创建,这是一个很好的解决方案。但这种情况非常罕见。只有在调用者实际处理虚拟对象时才应该这样做。

如果你返回一个空指针然后你发现你调用这个函数的每个地方都在写

MyObject* myobject=myfunction(whatever);
if (myobject==null) throw new PanicException;

然后你也可以在函数内部抛出异常。

更糟糕的是,如果你在写:

MyObject* myobject=myfunction(whatever);
if (myobject!=null)
{
  ... process it ...
}
else
{
   ... display error message ...
}

然后,您只是使用IF语句模拟异常处理。使用一个真正的例外。

另一方面,如果你抛出异常,然后你发现你经常写作:

MyObject* myobject;
try
{
  myobject=myfunction(whatever);
}
catch (PanicException pe)
{
  myobject=null;
}

那么,你最好只返回null。

我偶尔会创建虚拟对象。最常见的情况是函数返回集合(如数组或链表),如果找不到要放入集合的数据,则返回零元素的集合。然后调用者遍历集合中的元素,如果没有,那就没问题了。我有几个案例,我已经返回了一个带有零长度字符串的对象,用于名称或客户ID或其他任何内容。但一般来说,如果你只是返回一个虚拟对象,以便调用者可以测试并说,哦,它是一个虚拟对象,然后扔掉它,我认为你最好还是返回null。

当你说你只能在C ++ 11中返回空指针时,BTW不确定你的意思。传递空值的能力可以追溯到我见过的最早的C ++版本。

答案 3 :(得分:2)

应该使用第一个解决方案,除非阻止创建对象的条件是例外。否则返回NULL指针是一个完全有效的解决方案......即使在C ++ 11中也是如此。

“指针”当然应该使用RAII容器包装,例如std :: unique_ptr。你的代码应该是常见的做法。

如果你问我,第三种解决方案是完全浪费资源。您必须创建一个无效(无用)对象,并将其复制为返回值...仅用于丢弃它。