C ++构造函数:在初始化列表之前初始化局部变量

时间:2015-11-02 17:48:22

标签: c++

如何在构造函数(堆栈中)中存储初始化列表所需的临时状态?

例如,实现此构造函数......

// configabstraction.h
#include <istream>

class ConfigAbstraction
{
public:
    ConfigAbstraction(std::istream& input);

private:
    int m_x;
    int m_y;
    int m_z;
};

...使用像这样的有状态助手类?

// mysillyparserdontworry.h
#include <json/reader.h> //jsoncpp

class MySillyParserDontWorry
{
public:
    MySillyParserDontWorry(std::istream& input) { input >> m_parseTree; }
    int intByName(const char* name) const { return m_parseTree[name].asInt(); }

private:
    Json::Value m_parseTree;
};

我的尝试:

// configabstraction.cpp

ConfigAbstraction::ConfigAbstraction(std::istream& input)
    : local_parserState(input) // init local variable first: Not possible!
    , m_a(local_parserState.intByName("a"))
    , m_b(local_parserState.intByName("b"))
    , m_c(local_parserState.intByName("c"))
{
    MySillyParserDontWorry local_parserState; // ...because it is local
}

C ++的人为限制!

4 个答案:

答案 0 :(得分:5)

使用C ++ 11,您可以通过委派构造函数来解决这个问题:

class ConfigAbstraction
{
public:
    ConfigAbstraction(std::istream& input);

private:
    ConfigAbstraction(const MySillyParserDontWorry& parser);

    int m_a;
    int m_b;
    int m_c;
};

ConfigAbstraction::ConfigAbstraction(const MySillyParserDontWorry& parser)
    : m_a{parser.intByName("a")}
    , m_b{parser.intByName("b")}
    , m_c{parser.intByName("c")}
{
}

ConfigAbstraction::ConfigAbstraction(std::istream& input)
    : ConfigAbstraction{MySillyParserDontWorry{input}}
{
}

答案 1 :(得分:1)

为什么不简单地在构造函数的主体中进行赋值呢?

ConfigAbstraction::ConfigAbstraction(std::istream& input)
    : m_a(0)
    , m_b(0)
    , m_c(0)
{
    MySillyParserDontWorry local_parserState;
    m_a = local_parserState.intByName("a");
    m_b = local_parserState.intByName("b");
    m_c = local_parserState.intByName("c");
}

是否有任何特定要求阻碍您这样做?

  

C ++的人为限制!

这不是人为限制。如何在函数范围之外完成局部变量的初始化?它只会导致很大的混乱,其中变量实际上是初始化的(除了命名冲突)。

答案 2 :(得分:0)

解决问题的另一种方法是将三个int打包成一个公共数据结构。这将允许您使用私有静态帮助程序函数初始化该类型的对象。能够初始化对象而不是稍后分配对象也允许它为const(如果需要)。

以下是std::tuple的示例。但您也可以创建自己的助手struct甚至std::array<int, 3>;基本思想保持不变:有一个成员对象而不是三个。

#include <istream>
#include <tuple>

class MySillyParserDontWorry
{
public:
    MySillyParserDontWorry(std::istream& input) { /* ... */  }
    int intByName(const char* name) const { return /* ... */ 0; }

};

class ConfigAbstraction
{
public:
    ConfigAbstraction(std::istream& input);

private:

    static std::tuple<int, int, int> parse(std::istream& input)
    {
        std::tuple<int, int, int> result;
        MySillyParserDontWorry parser(input);
        std::get<0>(result) = parser.intByName("a");
        std::get<1>(result) = parser.intByName("b");
        std::get<2>(result) = parser.intByName("c");
        return result;
    }

    std::tuple<int, int, int> const m;
};


ConfigAbstraction::ConfigAbstraction(std::istream& input)
    : m(parse(input))
{
}

答案 3 :(得分:0)

您无法在成员之前初始化局部变量。原因很简单(因此它不是人为的限制):

必须在构造函数体开始之前初始化(构造)成员,因为构造函数体可能会访问它们 - 并且需要为此访问初始化它们。另一方面,在代码进入构造函数体之前,局部变量不存在(与任何其他函数一样)。 结论 - 在成员之前初始化局部变量是不可能的。