C ++初始化列表 - 参数来自构造函数体本身?

时间:2013-06-20 11:55:36

标签: c++

我有一些类似的代码:

#include <string>

class another_foo
{
public:
    another_foo(std::string str)
    {
        // something
    }

private:
    // something
};

class foo
{
public:
    foo();

private:
    another_foo obj;
};

foo::foo() : obj(str) // no `: obj("abcde")`, because it is not that simple in real situation.
{
    std::string str = "abcde"; // generate a string using some method. Not that simple in real situation.
    // do something
}

我将初始化objfoostr的私人成员。 但是这段代码无法编译。如何在初始化列表中使用构造函数体中的变量?

AFAIK,唯一的方法是将生成#include <string> class another_foo { public: another_foo(std::string str) { // something } private: // something }; class foo { public: foo(); private: another_foo obj; // std::string generate_str() // add this static std::string generate_str() // EDIT: add `static` to avoid using an invalid member { return "abcde"; // generate a string using some method. Not that simple in real situation. } }; foo::foo() : obj(generate_str()) // Change here { // do something } 的代码与构造函数分离为另一个函数,然后直接在初始化列表中调用该函数。那是......

{{1}}

但是有更好的方法吗?

4 个答案:

答案 0 :(得分:3)

是的,你必须将它移动到一个功能。如果它是一个单一用途的东西(仅用于此初始化)并且您可以访问C ++ 11 lambdas,则可以使用单用途lambda。否则,只需像你一样使用成员函数。请注意在那里调用虚函数,因为该对象仍在构建中。如果可能的话,最好使它static

Lambda示例:

class foo
{
public:
    foo();

private:
    another_foo obj;
};

foo::foo() : obj([] { return "abcde"; } ())
{
    // do something
}

答案 1 :(得分:2)

  

但是有更好的方法吗?

简而言之:不,如果你不愿意改变obj的分配方式或者它的语义,那就不行了。

你可以使用lambda来做这个的变体,比如使generate_str()为static,或者更好(如果代码很短):

foo::foo() : obj( []{ return "abcde"; }() )
{
}

HOWEVER:如果对象构造需要依赖于其他成员的逻辑,那么您必须确保成员的初始化顺序反映了相互依赖性(在独立的声明中对它们进行排序)对于家属)或更好:更改obj的语义或在堆上分配它。

更改分配会产生构建/销毁成本以及访问成本非常低,因此大多数时候它不是最佳解决方案,但它解决了问题:

class foo
{
public:
    foo();

private:
    std::unique_ptr<another_foo> obj; // no sharing of the instance

};

foo::foo() // obj is null
{
    // do something
    auto result_obj_info = compute_something();
    obj = new another_foo( result_obj_info ); 
    // OR if you use this http://stackoverflow.com/questions/12547983/is-there-a-way-to-write-make-unique-in-vs2012
    obj = std::make_unique<another_foo>( result_obj_info );
}

然而,我建议改变another_foo的语义,以便它具有值语义

#include <string>

class another_foo
{
public:

    another_foo(); // this create an invalid anoter_foo, unusable.

    another_foo(std::string str) // this create a valid another_foo
    {
        // something
    }

    // make sure copy/move functions are available, either automatically or manually

    bool is_valid() const;

private:
    // something
};

inline bool is_valid( const another_foo& obj ) { return obj.is_valid(); }

class foo
{
public:
    foo();

private:
    another_foo obj;
};

foo::foo()
{
    assert( is_valid( obj ) == false);
    std::string str = "abcde"; // generate a string using some method. Not just simple like that in real situation.
    // do something
    obj = another_foo( str );
    assert( is_valid( obj ) == true);
}

这样你的another_foo类型就像它的资源一样。如果不应该复制,只需将其移动即可。看一下std :: thread或std :: unique_ptr如何工作。例如。

答案 2 :(得分:1)

编写将在构造期间调用的成员函数时的风险是您可以在之后更改它以使用某些类的数据成员,这些成员在调用时可能尚未初始化。

最好定义一个外部函数来生成字符串,如下所示:

namespace {
std::string generate_str()
{
    return "abcde"; 
}
}

foo::foo() : obj(generate_str()) 
{
    // do something
}

这样,如果你必须将参数传递给函数,那么从构造函数中可以看到未初始化数据成员或虚拟成员函数返回值的使用,因此更容易捕获。

答案 3 :(得分:0)

如果它是特定于该类的常量,则可以这样定义:

class foo {
    public:
        foo();
    private:
        static const std::string STR;
        another_foo obj;
};

const std::string foo::STR = "abcde";

foo::foo() : obj(STR)
{
}

修改
由于它似乎不是常量,因此您可能必须为此作业使用静态成员函数。 (或者lambda,你的选择)

class foo {
    static std::string generate_string() const;
};

实现:

std::string foo::generate_string() const {
    return std::string("abcde");
}