C++ C++;构造函数:在初始化器列表之前初始化局部变量
如何在构造函数(堆栈上)中存储初始值设定项列表所需的临时状态 例如,实现此构造函数C++ C++;构造函数:在初始化器列表之前初始化局部变量,c++,C++,如何在构造函数(堆栈上)中存储初始值设定项列表所需的临时状态 例如,实现此构造函数 // configabstraction.h #include <istream> class ConfigAbstraction { public: ConfigAbstraction(std::istream& input); private: int m_x; int m_y; int m_z; }; 那么为什么不简单地在构造函数的主体中进行赋值呢
// configabstraction.h
#include <istream>
class ConfigAbstraction
{
public:
ConfigAbstraction(std::istream& input);
private:
int m_x;
int m_y;
int m_z;
};
那么为什么不简单地在构造函数的主体中进行赋值呢
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++!
这不是人为的限制。局部变量的初始化应该如何在其函数范围之外进行?这只会导致很大的混乱,变量实际上是初始化的(命名冲突除外)。解决问题的另一种方法是将三个独立的
int
s打包到一个公共数据结构中。这将允许您使用私有静态助手函数初始化该类型的对象。能够初始化对象而不是稍后分配给它也允许它是const
(如果需要)
下面是一个使用std::tuple
的示例。但是您也可以创建自己的helperstruct
甚至std::array
;基本思想保持不变:有一个成员对象而不是三个。
#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))
{
}
#包括
#包括
类mysillyparserdont
{
公众:
mysillyparserdontproiser(std::istream&input){/*…*/}
int intByName(const char*name)const{return/*…*/0;}
};
类配置抽象
{
公众:
配置抽象(std::istream&input);
私人:
静态std::元组分析(std::istream&input)
{
std::元组结果;
mysillyparserdont解析器(输入);
std::get(result)=parser.intByName(“a”);
std::get(result)=parser.intByName(“b”);
std::get(result)=parser.intByName(“c”);
返回结果;
}
std::元组常数m;
};
ConfigAbstraction::ConfigAbstraction(std::istream&input)
:m(解析(输入))
{
}
使用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}}
{
}
不能在成员之前初始化局部变量。原因很简单(因此这不是人为的限制): 必须在构造函数体开始之前初始化(构造)成员,因为构造函数体可能会访问它们,并且需要为此访问对它们进行初始化。另一方面,在代码进入构造函数体之前,局部变量不存在(对于任何其他函数)。
结论-在成员之前初始化局部变量是不可能的。将其作为成员变量?您不在构造函数主体中分配变量有什么原因吗?@NathanOliver:您不能在主体中初始化它们。您只能在那里分配它们。这在本例中是可能的,但如果成员是
const
@ChristianHackl-Oops,则不可能。修正了我的注释,而不是初始化。“什么是C++的人工限制!”——这个“人工限制”存在于我所知道的每种编程语言中(除了那些最初没有使用构造函数的自定义类型的那些)。它似乎不会阻止任何人编写好的、有效的和可读的代码。您可能希望将另一个构造函数设置为私有的。@ChristianHackl是的,我考虑过,但在本例中,有人可能希望直接调用它似乎是合理的。这很有趣。。。我认为这会使类更难使用,因为它迫使用户做出后果难以理解的选择。@ChristianHackl true,我可以选择任何一种方式,所以我做了你建议的编辑。“这只会导致很大的混乱,变量实际上是初始化的。”-欢迎来到编写糟糕的Java构造函数的世界:)@ChristianHackl好吧,编写糟糕的代码就是糟糕的。Java真的编译了这些东西并抛出运行时异常吗?我对Java不太熟悉。在Java中,您可以编写一个类,如classx{private final int I;X(){int j=1;I=j;}}
。编译器甚至不允许在初始化(或“赋值”)之前使用i
。它是100%安全和正确的,但我个人认为这是一个相当混乱的功能。也许这也是我的C++背景。如果初始化器可以省略,就在这种情况下,只有普通的旧数据成员,我想是的。但它们通常是无法避免的;我相信编译器会潜入初始值设定项列表中遗漏的任何默认构造函数。我更新了这个问题,以便使用初始值设定项列表是目标的一部分。我相信在执行初始值设定项列表的代码之前,会分配局部变量的内存。我称之为人工,C++阻止使用此代码来实现这个目的。
#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))
{
}
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}}
{
}