通过参数返回值是一个好的设计吗?

时间:2010-03-16 07:37:50

标签: c++ api

bool is_something_ok(int param,SomeStruct* p)
{
    bool is_ok = false;

    // check if is_ok

    if(is_ok)
       // set p to some valid value
    else
       // set p to NULL
    return is_ok;
}

如果“确实没事”,此函数返回true并将p设置为有效值 否则返回false并将p设置为NULL

这是一个好的或坏的设计?个人,我使用它时感到不舒服。 如果没有文件和评论,我真的不知道如何使用它。

BTW:是否有一些关于API设计的权威书籍/文章?

9 个答案:

答案 0 :(得分:13)

由于您已将问题标记为C ++而不是C,我建议您:

  • 直接返回值
  • 如果您有多个值,请使用输出参数
  • 尽可能使用非const引用作为输出参数(而不是指针),并对输入参数使用const-references。
  • 如果出现问题,请引发异常,而不是返回false或-1。

但这只是一些一般提示。最好的方法取决于具体的问题......

答案 1 :(得分:3)

取决于您希望如何处理'错误'。

例如,采用标准函数atoi。它将字符串转换为整数,但如果字符串不包含数字,它应该返回什么?在这种情况下,C / C ++运行时将设置errno全局变量。另一种方法是抛出异常。

就个人而言,我并不喜欢这两种选择。 因此,如果我通常使用以下规则:

  • 是否有可用于指示错误的可能返回值范围内的值,我是否只有1种可能的'种类'错误?在这些情况下,我使用返回值来指示错误。例如。如果找不到员工,像FindEmployee这样的函数可能只返回NULL。
  • 如果函数可以返回所有可能的值(如atoi示例中所示),则使用输出参数作为返回值,并让函数返回布尔值。如果您有多个可能的错误情况,请返回一个枚举,指示发生的错误(或成功)类型。

答案 2 :(得分:2)

我倾向于这样做。 您的示例中的替代方法是将两个事物编码为一个返回值(例如,使用NULL作为特殊值)或返回结构。

编码两件事有时是不可能的,而且容易出错。返回结构是一项额外的工作和混乱。所以我倾向于做你做过的事。我倾向于假设参数列表中的“原始”指针和引用用于返回值,如果它们仅用于传入数据,它们将是“const”。

但说实话,我经常忘记这条规则,所以也许这不是一个很好的规则。

升级库中有一个类“可选”,可能符合您的需求,但我自己从未真正喜欢它,因为可能没有很好的理由。

答案 3 :(得分:2)

我想说这取决于。复制构造的类型有多贵?你能写出你的功能RVO - 友好吗?至少在我们使用rvalue references的C ++ 0x之前,我的建议是不返回“昂贵”类型(例如std::vector<std::string>),而是将它们作为引用传递,例如使用方法:

void split(const std::string &txt, char sep, std::vector<std::string> &out);

而不是:

std::vector<std::string> split(const std::string &txt, char sep);

根据您编写函数的方式,RVO可能会启动,但根据我的经验,这并不是您通常可以依赖的。

答案 4 :(得分:2)

关于您关于API设计的书的问题。寻找Martin Reddy于2011年发布的“API Design for C ++”。

作为对已接受答案的评论。在本书中,作者实际上建议更喜欢用于输入参数的const引用和用于输出参数的指针,因为它更明确地指示客户端可以修改参数,例如, foo(bar)vs. foo(&amp; bar)。

您也可以观看演讲How To Design A Good API and Why it Matters。其中主要使用Java,但我记得。

答案 5 :(得分:1)

我认为如果某些内容不正确则返回NULL并且如果在这种情况下确定更好则使用SomeStruct

SomeStruct* is_something_ok(int param);

在这种情况下,除了检查布尔值之外,你应该检查它是否为NULL,如果不使用它。

但是有些情况下你必须通过参数返回值。它取决于返回值的数量,并且可以使用上下文函数。

答案 6 :(得分:1)

您可以执行以下操作:

bool is_something_ok(int param,SomeStruct* p);

// return NULL if the operation failed.
Somestruct* do_work(int param);

很难说设计/ API是好还是坏,没有什么是黑色或白色......(灰色?!?!?)

您必须选择 更容易编码的API /标准。 是连贯的,如果您选择第一种方法类型,请为项目的其余部分执行此操作。

也不要忘记来记录您的代码,因此了解如何使用您的API会更容易。

答案 7 :(得分:1)

我建议直接返回Result-Type:

SomeStruct doSomething(int param) {...}

并在函数无法处理的情况下抛出异常(tux21b已经以这种方式提到过)。或者,您可以使用std::pair返回两种类型,而不会抛出异常,如下所示:

pair<SomeStruct, bool> doSomething(int param) {...}

第三,我喜欢将输出参数声明为指针而不是引用(如你所提到的),因为在调用代码中我看到了输入和输出参数的差异。鉴于功能:

void doSomething(const Somestruct& in, Somestruct* out) {...}

然后在调用代码中它是可见的(不看函数声明)输入是什么以及输出参数是什么(如果我最终应用这个概念)。

SomeStruct a;
SomeStruct b;
doSomething(a, &b); // you see a is input, b is output

答案 8 :(得分:0)

如果只想在成功时返回值,则应查看boost::optional