C++ 在静态函数中返回对象而不是构建对象有什么好处?

C++ 在静态函数中返回对象而不是构建对象有什么好处?,c++,constructor,static-methods,C++,Constructor,Static Methods,以以下Qt框架文档为例,即使我的问题不是特定于Qt的: 您可以找到静态公共成员函数: QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr) 而不是: QVersionNumber(const QString &string, int *suffixIndex = nullptr) 在构造函数列表中 我在许多库API中看到过这种选择,我无法理解它的优点和缺少构造函数的原因。静态

以以下Qt框架文档为例,即使我的问题不是特定于Qt的:

您可以找到静态公共成员函数:

QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr)
而不是:

QVersionNumber(const QString &string, int *suffixIndex = nullptr)
在构造函数列表中


我在许多库API中看到过这种选择,我无法理解它的优点和缺少构造函数的原因。

静态创建方法很方便,因为它们不需要创建冗余对象,并且可以添加到它们创建的对象的代码中。使用creational方法有很多原因替代构造函数。以下是一些:

  • 构造函数必须始终使用有效的对象创建完成。在构造函数中使用异常是一种非常糟糕的做法。如果无法创建对象,例如由于不正确的参数或不正确的初始化顺序,我们该怎么办?对于创建方法,您可以简单地返回
    nullptr
    或一些“错误”的对象类型

  • 如果我们想自定义对象创建而不更改其代码。例如,我们想要构建一个对象并对其进行配置,或者我们没有可用参数的构造函数。包装器函数允许您在不涉及类代码的情况下解决问题。这适用于给定的示例,
    fromString
    在构造函数调用之前解析字符串参数

  • 我们不知道要构造什么样的对象。这是在运行时决定的。通常,我们可以返回基对象,并在包装器内选择必要的类。这允许您不更改具体对象选择的主代码

  • 我们希望控制所有对象的创建,以消除可能的内存泄漏。例如,在取消初始化库时,释放所有资源,即使用户忘记这么做


  • 我建议您熟悉设计模式,尤其是。

    这实际上是一个很好的问题

    原因因场景而异,但涉及以下问题:

    • 构造函数的可读性如何
    • 我是在创建人工对象还是不必要地复制条件,以便根据初始化器列表编写构造函数
    • 我的构造器是否可能与用于执行不同类型构造的构造器存在歧义
    • 我的构造函数不用在代码中拼出一个名字就可以做什么
    所有这些都会涉及到一定程度的主观性

    假设的例子,猜测QVersionNumber的内部结构

    你会如何在初始化列表中写下这些术语

    QVersionNumber 
    QVersionNumber::fromString(const QString &string, int *suffixIndex)
    {
        std::optional<QVersionNumber> result;
        auto first = string.begin();
        auto last = string.end();
        auto opt_major = maybe_extract_decimal(first, last); // modifies first
        if (not opt_major.has_value())
            result.emplace();
        else
        {
            auto opt_minor = maybe_extract_decimal(first, last); // modifies first
            if (not opt_major.has_value())
                result.emplace(*opt_major);
            else
                result.emplace(*opt_minor);
        }
        if (suffixIndex)
          *suffixIndex = int(std::difference(string.begin(), first);
        return *std::move(result);
    }
    
    QVersionNumber
    QVersionNumber::fromString(常量QString&string,int*subfixindex)
    {
    std::可选结果;
    auto first=string.begin();
    auto last=string.end();
    auto opt_major=maybe_extract_decimal(first,last);//首先修改
    if(非opt_major.has_value())
    结果:emplace();
    其他的
    {
    auto opt_minor=maybe_extract_decimal(first,last);//首先修改
    if(非opt_major.has_value())
    结果:安置(*选择专业);
    其他的
    结果:安置(*选择_小调);
    }
    if(后缀索引)
    *suffixIndex=int(std::difference(string.begin(),first);
    返回*标准::移动(结果);
    }
    

    这当然是可能的,遵从采用专门函数对象的私有构造函数。但库的作者可能认为这太神秘或难以维护。

    构造函数可能采用比传递到该函数中的参数更奇特的参数。这些额外的参数可能需要一些额外的设置代码t不适合构造函数本身。它使参数更显式,例如
    静态角度角度::fromdege(float);
    静态角度角度::fromdegradian(float);
    @Jarod42:是的,它有意义它们可以被视为自由函数,@Jarod42不够显式,
    静态角度角度::From(Degree);静态角度::From(弧度);
    用于1。工厂直接返回对象,因此
    nullptr
    是不可能的,异常也可以。@Jarod42,是的,但根据我的理解,API提供了单独的构造函数来创建“空版本”如果可以解析sting,则
    fromString
    函数使用整数列表中的构造函数,或者
    QVersionNumber()
    在另一种情况下。这与第1点和第2点非常相关。第3点是错误的。在上面的示例中,函数返回的不是指向的指针,而是平面对象。尝试进行“变形”,你会得到一些很好的切片。第4点似乎与问题无关。@bitmask,你是如何决定列出所有项目的参考作者的示例?我已经列出了在API中使用它的一般原因。在作者案例中,我认为它与p2有关。@DmytroDadyka:使用常规构造函数也可以转发到其他构造函数。