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

C++ constructor: Initialize local variable before initializer list

本文关键字:初始化 局部变量 列表 C++ 构造函数      更新时间:2023-10-16

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

例如,实现这个构造函数…

// 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++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}}
{
}

问题的另一种解决方案是将三个单独的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))
{
}

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

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++的人为限制

这不是一个人为的限制。局部变量的初始化应该如何在其函数范围之外进行?这只会导致一个巨大的混乱,变量实际上是初始化的(命名冲突除外)。

不能在成员之前初始化局部变量。原因很简单(所以这不是人为的限制):

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