命名构造函数和临时生命周期扩展

时间:2012-07-06 16:00:33

标签: c++ named-constructor

我正在寻找一种方法来定义一个“基础”构造函数,它将使用默认值初始化值,然后将该基础扩展为许多专门的构造函数。

我想要的伪代码可能是这样的:

class Foo{
private:
    int val;
    /* ... */

public:
    // Base constructor
    Foo(){ /*...*/ }           // This provides basic initialization of members

    // Named constructors
    static Foo fromString(string s){
        Foo f;                 // Call base constructor
        f.val = s.length();    // Customize base object
        return f;              // Return customized object
    }
    static Foo fromInt(int v){
        Foo f;
        f.val = v;
        return f;
    }
}

起初,我考虑过extending the lifetime of the temporary f,但const声明阻止我编辑其成员。所以看来这已经结束了。

然后我尝试了"named constructor"方法(如上所示)。但是,我必须先修改示例以创建对象,然后修改它,然后返回它。这似乎有效,但我有理由相信这只是一个巧合,因为f是一个临时的,并且在函数结束时超出了范围。

我也考虑使用像auto_ptr这样的东西,但是我正在使用Foo对象以及auto_ptr到Foo,这使得其余的代码“关心” “对象是通过基础构造函数创建的(在这种情况下它是一个对象),还是通过一个扩展构造函数(在这种情况下它将是一个指针)。

如果它有用,在Python中,我会使用这样的东西:

class Foo(object):
    def __init__(self):
        /* Basic initialization */

    @classmethod
    def fromString(cls, s):
        f = Foo() #†
        f.val = len(s)
        return f

最后,我想用这种方式做两件事:

  1. 代码重用,我想将每个构造函数的公共初始化移到一个构造函数中。我意识到我可以通过每个构造函数调用的init()类型私有方法来实现这一点,但我只是想提一下。
  2. 清晰明确并解决歧义。与命名构造函数示例的动机非常相似,参数类型本身不足以确定应该使用哪个ctor。此外,fromSomething语法提供了极好的清晰度。
  3. 请原谅我,如果有一个简单的解决方案,我的工作在过去几年已从c ++转移到Java / Python,所以我有点生疏。

3 个答案:

答案 0 :(得分:6)

这完全有效:

static Foo fromInt(int v){
    Foo f;
    f.val = v;
    return f;
}

这会在您返回Foo时调用f的复制构造函数(可能编译器应用return value optimization,因此不会复制)。 f超出了范围,但返回值只是它的副本,所以这是完全有效的,它不仅仅是“巧合”它的工作。

因此,如果你担心使用命名的构造函数方法只是你不知道它是否有效,那么继续它,它可以很好地工作。

答案 1 :(得分:2)

在C ++ 11中,您可以从构造函数中调用其他构造函数:

struct X{
  X() : ... { ... }
  X(int i) : X() { ... }
  X(std::string s) : X() { ... }
};

对于C ++ 03,命名的构造函数方法可能是最好的,也是完全合理的,恕我直言。

答案 2 :(得分:0)

为什么不:

class Foo{
private:
    int val;
    void Init(int v = <some default value>/*What ever here *));
    /* ... */


public:
    // Base constructor
    Foo(){ Init(); }           // This provides basic initialization of 
    Foo(string &s) { Init(s.length); };
    Foo(int v) { Init(v); };
};

似乎更简单。