代码之家  ›  专栏  ›  技术社区  ›  aztack

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

  •  22
  • aztack  · 技术社区  · 15 年前
    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,并在“something is ok”时将p设置为有效值 否则返回false并将p设置为null

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

    顺便问一下:有没有关于api设计的权威书籍/文章?

    9 回复  |  直到 13 年前
        1
  •  13
  •   tux21b    15 年前

    既然你把问题标记为C++而不是C,我建议你:

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

    但这只是一些一般性的暗示。最好的方法总是取决于具体的问题…

        2
  •  3
  •   Patrick    15 年前

    取决于您想如何处理“错误”。

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

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

    • 在可能的返回值范围内是否有可用于指示错误的值,并且我是否只有1个可能的“种类”错误?在这些情况下,我使用返回值来指示错误。例如,如果找不到雇员,像findeEmployee这样的函数可以简单地返回null。
    • 如果函数可以返回所有可能的值(如atoi示例中所示),则使用输出参数作为返回值,并让函数返回布尔值。如果有多个可能的错误情况,请返回一个枚举,指示发生的错误(或成功)的类型。
        3
  •  2
  •   jcoder    15 年前

    我倾向于这样做。 示例中的另一种方法是将两个对象编码为一个返回值(例如使用null作为特殊值)或返回一个结构。

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

    但老实说,我一想起这条规则就忘得一干二净,所以也许这条规则不太好。

    boost库中有一个“可选”类,可以满足您的需要,但我自己从来没有真正喜欢过它,也许没有什么很好的理由。

        4
  •  2
  •   Andreas Magnusson    15 年前

    我想这要看情况而定。复制构造的类型有多贵?你能写下你的函数吗 RVO -友好?至少在我们有C++ 0x之前 rvalue references ,我的建议是不要返回“昂贵”类型(例如 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可能会启动,但根据我的经验,这不是你通常可以依赖的东西。

        5
  •  2
  •   Hoschie0815    13 年前

    关于你在一本关于api设计的书中提出的问题。寻找Martin Reddy的“API设计”,它于2011出版。

    作为对接受答案的评论。在这本书中,作者实际上建议输入参数更倾向于常量引用,输出参数更倾向于指针,因为它更明确地指示了客户机可以修改参数,例如foo(bar)对foo(bar)。

    你也可以看谈话 How To Design A Good API and Why it Matters . 它主要使用Java,但我记得。

        6
  •  1
  •   Arsen Mkrtchyan    15 年前

    我认为如果有不好的地方就返回null,如果好的话就返回somestruct

    SomeStruct* is_something_ok(int param);
    

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

    但有时必须按参数返回值。它取决于返回值的数量,并且可以使用上下文函数。

        7
  •  1
  •   Phong    15 年前

    您可以执行以下操作:

    bool is_something_ok(int param,SomeStruct* p);
    

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

    很难说一个设计/api是好是坏,没有什么是黑是白…(灰色)??????)

    您必须选择将 更容易编码 用。和 连贯 ,如果选择第一种方法类型,请在项目的其余部分执行此操作。

    别忘了 记录代码 ,因此更容易理解如何使用api。

        8
  •  1
  •   Christian Ammer    15 年前

    我建议直接返回结果类型,如下所示:

    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
    
        9
  •  0
  •   Motti    15 年前

    你应该调查一下 boost::optional 当你只想在成功时返回一个值时。