在结构体中初始化默认值

107
如果我只需要初始化 C++ 结构体中的几个值,那么以下代码是否正确:
struct foo {
    foo() : a(true), b(true) {}
    bool a;
    bool b;
    bool c;
 } bar;

我可以这样理解吗,最终我会得到一个名为barstruct项目,其中包含bar.a = truebar.b = true 和未定义的 bar.c元素?


这个“bar”只是一个重命名,如果你在使用C++,你不需要以这种方式做事。 - aaronman
14
@aaronman,不,'bar' 是一个变量。 - Luchian Grigore
5
@aaronman,我认为你把这个和 typedef struct foo {} bar; 混淆了。 - greatwolf
1
那么,如果bar是结构体,那么foo是什么?这和定义一个foo结构并单独声明一个类型为foo的新变量bar是一样的吗? - Vince
5
Bar只是一个foo对象。它与struct foo {//something};foo bar; 相同。 - Yan Yi
相关:如何在C++中将结构体初始化为0 - Gabriel Staples
4个回答

309

你甚至不需要定义构造函数

struct foo {
    bool a = true;
    bool b = true;
    bool c;
 } bar;

为了澄清:这些被称为大括号或等号初始化程序(因为您也可以使用大括号初始化代替等号)。 这不仅适用于聚合物:您还可以在普通类定义中使用它。 这是在C++11中添加的。


140
下投票者:在指责人们合法或不合法之前,请先了解C++11标准的更新。它实际上可以正常编译。 这是C++11,而C++11就是C++,除非采用了新版本。 - Ben Voigt
9
自2011年8月21日起,“c++”标签意味着C++11。如果人们想讨论旧版本,可以使用“C++98”或“C++03”。明年的某个时候,“c++”标签将再次更改为表示C++14。 - Ben Voigt
12
值得注意的是,这立即使该类型非POD(通过std::is_pod<T>::value进行验证),因此在这个意义上,它相当于编写一个非默认构造函数,在初始值列表中设置相同的值。 - underscore_d
3
实际上,在 C++11 中只有在没有默认成员初始化器的情况下才允许大括号初始化。这个限制在 C++14 中被解除了。ideone 使用的是 C++14,但如果你使用任何 C++11 编译器,那么代码将会失败。http://en.cppreference.com/w/cpp/language/aggregate_initialization - Giovanni Botta
如果结构体是使用typedef定义的,就像C语言风格一样,它仍然会被初始化吗? - Dumbo

46

是的。 bar.abar.b 被设置为 true,但 bar.c 未定义。然而,某些编译器会将其设置为 false。

在此处查看实时示例:struct demo

根据 C++ 标准第 8.5.12 节:

如果没有进行初始化,则具有自动或动态存储期的对象具有不确定值

对于基本内置数据类型(bool、char、wchar_t、short、int、long、float、double、long double),只有全局变量(所有静态存储变量)在未显式初始化时才获得零的默认值。

如果您真的不想从未定义的状态开始使用 bar.c,您也应该像对 bar.abar.b 做的那样对其进行初始化。


1
@CaptainObvlious刚更新了,抱歉我被其他事情分散注意力,没有在电脑旁。 - taocp
5
不是"未定义(undefined)",而是"未初始化(uninitialized)",意味着它具有不确定的值。 - Jonathan Wakely
我认为OP的意思是_value_未定义,而不是成员。 - srujzs

12

你可以通过使用构造函数来实现,像这样:

struct Date
{
int day;
int month;
int year;

Date()
{
    day=0;
    month=0;
    year=0;
}
};

或者像这样:

struct Date
{
int day;
int month;
int year;

Date():day(0),
       month(0),
       year(0){}
};

在您的情况下,bar.c未定义,并且其值取决于编译器(而a和b被设置为true)。


16
没有理由更喜欢第一种形式。你应该使用初始化列表。 - jamesdlin
1
@jamesdlin 在99.9%的情况下是同意的,第二种方法由于无数原因而更可取。然而,只是为了扮演魔鬼的代言人! - 如果成员不是int类型,而是必须满足平凡定义(例如没有非默认构造函数)的自定义类,那么将在函数体中分配而不是在初始化列表中构造就有理由了。然而,这种情况有用的场景数量很少,所以初始化列表应该是默认推荐的方式 - 除非经过仔细的限定和证明,否则不应使用这种方法。 - underscore_d
1
@underscore_d 不,这不是一个很好的理由。你可以在所有情况下使用初始化列表,除了成员变量相互依赖的情况,这是违反3NF的规定;-) - user207421
@EJP,这完全可能是一个原因;我已经需要它好几次了。我认为你关注的是父级初始化,但我说的是它的成员。对于成员对象,如果其default构造函数不合适且标准布局排除非default构造函数,则必须以另一种方式执行“初始化”,无论是operator=还是init函数或其他方式。在父类的构造函数中进行此“初始化”是有意义的,即在其主体中分配。这里的operator=等同于帖子中原始赋值的in-body分配。也许有点过度思考或离题,但这是真的;-) - underscore_d
@jamesdlin,第一种形式对于明确初始化数组的值非常有价值。更新...或者在初始化列表中也可以实现吗? - Gabriel Staples
1
如果您正在初始化数组的单个元素,则确实需要在构造函数体中执行此操作。显然,某些事情必须在构造函数体中完成。我之前的评论是针对此答案中的特定示例的。 - jamesdlin

4

显式默认初始化可以帮助解决问题:

struct foo {
    bool a {};
    bool b {};
    bool c {};
 } bar;

行为 bool a {}bool b = bool(); 相同,都会返回 false


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接