HI, 我有一个问题,但很长一段时间都找不到答案,即 关于参数的以下2个陈述之间有什么区别 初始化?
class A::A()
: a(0), b(0), c(0)
{
}
class A::A()
{
a = 0
b = 0;
c = 0;
}
我知道有“直接初始化”和“复制初始化”,但我 不知道其他差异是什么以及是否有任何描述 关于第一个声明?
提前致谢
答案 0 :(得分:24)
使用初始化列表,只使用给定值创建和初始化成员一次。
使用赋值,成员初始化为默认值,然后在构造函数体中重新分配。
在某些情况下(常量,引用),您只能使用初始化列表,因为
在其他情况下,即使可以进行分配,初始化列表仍然是可取的,因为它避免了额外的工作(双重初始化,对于某些类型来说可能代价很高)并且遵循一个常见的习惯用法,使您的代码更易于理解和维护
一个值得注意的警告是,成员初始化的顺序由它们的声明顺序定义,不按初始化列表中列出的顺序定义。 E.g。
class Example {
int a, b, c;
Example() : a(1), c(2), b(c) {}
}
产生未定义的行为,因为b
在 c
之前初始化,因此具有未定义的值。为了避免混淆以及可能存在这些微妙的错误,请始终按照在类中声明的顺序列出初始化列表中的成员。
这一开始可能看起来很模糊,但这是有道理的。在C ++中,保证类的所有成员都以与创建它们的顺序完全相反的方式被销毁。现在,类可以有多个构造函数,每个构造函数都有自己的初始化列表,并且(不幸的是,有人可能会说)初始化列表可以按程序员想要的任何方式进行排序。如果初始化列表的顺序确定了初始化的实际顺序,则运行时应以某种方式维护有关每个对象的数据,以便记住它所创建的构造函数,以及应该以何种顺序销毁其成员。这会产生运行时开销,因为没有明显的好处,所以 - 符合一般的C ++哲学“仅为你使用的东西付费” - 决定初始化顺序由声明顺序一劳永逸地定义。
答案 1 :(得分:5)
默认初始化列表的目的是在类中初始化常量变量。 因为常量变量在初始化对象之前初始化。
我提供了一个样本来解释这两个初始化之间的区别:
class A
{
private:
const int x;
};
A::A():x(5) //this code works fine
{
}
A::A() //this code is wrong.const variable is not initialized once object
{
x=5;
}
答案 2 :(得分:1)
主要区别在于,在第一种情况下,clas成员已初始化,在第二种情况下,已分配。对于非整数类型,这意味着,在第二种情况下,您将使用operator=
为您的班级成员分配值。
通常,首选使用第一种情况,因为在这种情况下,类构造符在构造函数体之前初始化。
此外,您无法在多种情况下使用赋值,例如,当声明类成员 const 时。
答案 3 :(得分:1)
来自Marshall Cline的section 10.6 of the C++ FAQ:
初始化列表。事实上, 构造函数应该初始化为 规则中的所有成员对象 初始化列表。一个例外是 进一步讨论了。
考虑以下构造函数 初始化成员对象x_ 使用初始化列表: Fred :: Fred():x_(无论如何){}。该 这样做最常见的好处是 改善了表现。例如,如果 什么是相同的表达 键入成员变量x_,结果 无论表达是什么 直接在x_里面构造 - 编译器不会单独复制 对象。即使类型是 不一样,编译通常是 能够做得更好 初始化列表比用 分配。
另一种(效率低下)的构建方式 构造函数是通过赋值来实现的 as:Fred :: Fred(){x_ = what; }。 在这种情况下表达无论如何 导致一个单独的临时对象 被创建,这个临时对象 传递给x_对象 赋值运算符。然后 临时对象被破坏了 ;。那效率很低。
好像那还不够糟糕,那就是 另一个效率低下的原因 在构造函数中使用赋值: 成员对象将完全得到 由默认构造 构造函数,这可能是 例如,分配一些默认金额 内存或打开一些默认文件。 如果,所有这些工作都可能是徒劳的 无论表达和/或 赋值运算符导致对象 关闭该文件和/或释放该文件 记忆(例如,如果是默认值 构造函数没有分配大 足够的内存池或如果它打开 错误的文件)。
结论:所有其他事情都存在 等于,如果你的代码运行得更快 你使用初始化列表 而不是任务。
注意:没有表现 如果x_的类型是一些的差异 内置/内在类型,例如int 或char *或浮点数。但即使在这些 个案,我个人的偏好是 设置那些数据成员 初始化列表而不是via 分配一致性。另一个 对称性论证有利于使用 初始化列表甚至为 内置/内在类型:非静态 const和非静态参考数据 成员无法分配值 构造函数,所以对称它 有意义地初始化一切 在初始化列表中。
现在是例外。每条规则都有 例外(嗯;确实“每条规则都有 例外“有例外吗?提醒 我对哥德尔的不完整性 定理),还有几个 “使用初始化的例外” 列出“规则。底线是使用 常识:如果它更便宜,更好, 更快,等等不使用它们,然后通过 一切,不要使用它们。这有可能 当你的班级有两个时发生 需要初始化的构造函数 该对象的数据成员 不同的订单。或者它可能发生 当两个数据成员是 自我指涉。或者当一个 data-member需要引用 这个对象,你要避免一个 关于使用它的编译器警告 {开头的{之前的关键字 构造函数的主体(当你的 特定的编译器碰巧问题 特别警告)。或者当你 需要对a进行if / throw测试 变量(参数,全局等) 在使用该变量之前 初始化你的一个成员。 这份清单并非详尽无遗;请 别写我要我加 另一个“或者什么时候......”。重点是 简单地说:使用常识。