c++ 中初始化程序和默认初始化程序列表之间的区别

发布于 2024-11-04 03:55:24 字数 257 浏览 0 评论 0原文

你好, 我有一个问题但是找了好久都找不到答案,那就是 下面关于参数的两种说法有什么区别 初始化?

class A::A() 
    : a(0), b(0), c(0) 
{ 
}

class A::A() 
{ 
    a = 0 
    b = 0; 
    c = 0; 
} 

我知道有“直接初始化”和“复制初始化”,但我 不知道还有什么区别以及是否有任何描述 关于第一个声明?

提前致谢

HI,
I have a question but cannot find the answer for a long time, that is,
what's difference between the following 2 statements about the parameter
initialization?

class A::A() 
    : a(0), b(0), c(0) 
{ 
}

class A::A() 
{ 
    a = 0 
    b = 0; 
    c = 0; 
} 

I know there is "direct initialization" and "copy initialization", but I
don't know what's the other differences and if there is any description
about the first statement?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晨曦慕雪 2024-11-11 03:55:24

使用初始值设定项列表,仅使用给定值创建和初始化成员一次。

使用赋值,成员会使用默认值进行初始化,然后在构造函数主体中重新赋值。

在某些情况下(常量、引用),您只能使用初始化列表,因为

  • 它们必须使用有效值进行初始化,并且
  • 一旦构造完毕,重新分配它们是非法的。

在其他情况下,即使可以进行赋值,初始化列表仍然更可取,因为它避免了额外的工作(双重初始化,这对于某些类型来说可能成本高昂)并遵循常见的习惯用法,使您的代码更易于理解和维护。

需要注意的是,成员的初始化顺序是由其声明顺序定义的,而不是由它们在初始化列表中列出的顺序定义。例如,

class Example {
  int a, b, c;

  Example() : a(1), c(2), b(c) {}
}

会产生未定义的行为,因为 b 是在 c 之前初始化的,因此具有未定义的值。为了避免混淆和潜在的此类微妙错误,请始终按照与在类中声明的顺序相同的顺序在初始化列表中列出成员。

乍一看这似乎很晦涩,但这是有原因的。在 C++ 中,可以保证类的所有成员都按照与创建顺序完全相反的顺序被销毁。现在,类可以有多个构造函数,每个构造函数都有自己的初始化列表,并且(不幸的是,有人可能会说)初始化列表可以按程序员想要的任何方式排序。如果初始化列表的顺序决定了初始化的实际顺序,则运行时应该以某种方式维护有关每个对象的数据,以记住它是使用哪个构造函数创建的,以及应以什么顺序销毁其成员。这会产生运行时开销,但没有明显的好处,因此 - 符合“只为你使用的东西付费”的一般 C++ 哲学 - 决定初始化顺序由声明顺序一劳永逸地定义。

Using the initializer list, the members are created and initialized only once, with the given value.

Using assignment, the members are initialized with a default value, then reassigned in the constructor body.

In some cases (constants, references) you can only use initialization lists because

  • these must be initialized with a valid value, and
  • once they are constructed, reassigning them is illegal.

In other cases, even though assignment is possible, initialization list is still preferable as it avoids extra work (double initialization, which can be costly for some types) and adheres to a common idiom, making your code easier to understand and maintain.

One caveat which is good to know is that the order of initialization of members is defined by their order of declaration, not by the order in which they are listed in the initialization list. E.g.

class Example {
  int a, b, c;

  Example() : a(1), c(2), b(c) {}
}

yields undefined behaviour because b is initialized before c, hence with an undefined value. To avoid confusion and the potential for such subtle bugs, always list members in the initialization list in the same order as they're declared in the class.

This may seem obscure at first, but there is a reason for it. In C++, it is guaranteed that all members of a class are destroyed in exactly the reverse of the order they were created. Now, classes can have multiple constructors, each with its own initialization list, and (unfortunately, one might say) initialization lists can be ordered any way the programmer wants. If the order of the initialization list determined the actual order of initialization, the runtime should somehow maintain data about each object to remember which constructor it was created with, and in what order its members should be destroyed. This would incure runtime overhead, for no obvious benefit, so - in line with the general C++ philosophy of "pay only for what you use" - it was decided that the initialization order is defined by the declaration order once and for all.

月亮是我掰弯的 2024-11-11 03:55:24

默认初始化列表的目的是初始化类中的常量变量。
因为常量变量是在对象初始化之前初始化的。

我提供一个示例来解释这两种初始化之间的区别:

 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;
    }

The purpose of default initialization list is to initialize the constant variable with in the class.
Because the constant variable is initialized before the object is initialized.

I provide one sample to explain the difference between these two initializations:

 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;
    }
风轻花落早 2024-11-11 03:55:24

主要区别在于,在第一种情况下,类成员被初始化,而在第二种情况下,它们被分配。对于非整数类型,这意味着在第二种情况下,您将使用 operator= 为类成员赋值。

通常,最好使用第一种情况,因为在这种情况下,类成员在构造函数主体之前初始化。

另外,在某些情况下不能使用赋值,例如,当类成员声明为 const 时。

The main difference is, that in first case the clas members are initialized and in the second case they are assigned. For non integral types it means, that in the second case you will use operator= to assign values to your class members.

Commonly, it's preferred to use first case, because in that case class memebers are initialized before the constructor body.

Also, you can't use assignment on several cases, for example, when class member is declared const.

愚人国度 2024-11-11 03:55:24

摘自 Marshall Cline 的 C++ 常见问题解答第 10.6 节

初始化列表。实际上,
构造函数应该初始化为
规则中的所有成员对象
初始化列表。一个例外是
下面进一步讨论。

考虑以下构造函数
初始化成员对象 x_
使用初始化列表:
Fred::Fred() : x_(随便) { }。这
这样做最常见的好处是
提高性能。例如,如果
表达式无论什么都一样
输入为成员变量x_,结果
无论什么表达方式
直接在 x_ 内部构造——
编译器不会制作单独的副本
的对象。即使类型是
不一样,编译器通常是
能够做得更好
初始化列表比
作业。

另一种(低效)构建方式
构造函数是通过赋值来实现的,比如
如: Fred::Fred() { x_ = 随便; }。
在这种情况下,表达式无论
导致一个单独的临时对象
被创建,并且这个临时对象
被传递到 x_ 对象的
赋值运算符。然后那个
临时对象被破坏
;.这效率很低。

好像这还不够糟糕,还有
另一个低效率的来源
在构造函数中使用赋值:
成员对象将完全获得
按其默认构造
构造函数,这可能是
例如,分配一些默认金额
内存或打开一些默认文件。
如果,所有这些工作都可能毫无意义
无论什么表达和/或
赋值运算符导致对象
关闭该文件和/或释放该文件
内存(例如,如果默认
构造函数没有分配一个大的
足够的内存池或者是否已打开
错误的文件)。

结论:所有其他事情都是
相等,如果
你使用初始化列表而不是
比分配。

注意:没有任何表现
如果 x_ 的类型是 some 则不同
内置/固有类型,例如 int
或 char* 或 float。但即使在这些
情况下,我个人的偏好是
设置这些数据成员
初始化列表而不是via
分配以保持一致性。其他
支持使用的对称性论证
初始化列表甚至
内置/固有类型:非静态
const 和非静态参考数据
无法为成员分配值
构造函数,因此为了对称性
初始化一切都是有意义的
在初始化列表中。

现在来说说例外情况。每条规则都有
例外(嗯;“每条规则都有
例外”有例外吗?提醒
我对哥德尔不完备性的看法
定理),并且有几个
“使用初始化
列出“规则。底线是使用
常识:越便宜越好
更快等不使用它们,然后通过
所有手段,都不要使用它们。这可能
当你的班级有两个人时发生
需要初始化的构造函数
该对象的数据成员在
不同的命令。或者它可能会发生
当两个数据成员是
自我参照。或者当一个
数据成员需要引用
这个对象,并且你想避免
关于使用 this 的编译器警告
开始 { 之前的关键字
构造函数的主体(当你的
特定编译器碰巧发出
该特定警告)。或者当你
需要对 a 进行 if/ throw 测试
变量(参数、全局等)
在使用该变量之前
初始化您的 this 成员之一。
此列表并不详尽;请
不要写信给我要求我添加
另一个“或者当……”。重点是
简而言之:运用常识。

Taken from section 10.6 of the C++ FAQ by Marshall Cline:

Initialization lists. In fact,
constructors should initialize as a
rule all member objects in the
initialization list. One exception is
discussed further down.

Consider the following constructor
that initializes member object x_
using an initialization list:
Fred::Fred() : x_(whatever) { }. The
most common benefit of doing this is
improved performance. For example, if
the expression whatever is the same
type as member variable x_, the result
of the whatever expression is
constructed directly inside x_ — the
compiler does not make a separate copy
of the object. Even if the types are
not the same, the compiler is usually
able to do a better job with
initialization lists than with
assignments.

The other (inefficient) way to build
constructors is via assignment, such
as: Fred::Fred() { x_ = whatever; }.
In this case the expression whatever
causes a separate, temporary object to
be created, and this temporary object
is passed into the x_ object's
assignment operator. Then that
temporary object is destructed at the
;. That's inefficient.

As if that wasn't bad enough, there's
another source of inefficiency when
using assignment in a constructor: the
member object will get fully
constructed by its default
constructor, and this might, for
example, allocate some default amount
of memory or open some default file.
All this work could be for naught if
the whatever expression and/or
assignment operator causes the object
to close that file and/or release that
memory (e.g., if the default
constructor didn't allocate a large
enough pool of memory or if it opened
the wrong file).

Conclusion: All other things being
equal, your code will run faster if
you use initialization lists rather
than assignment.

Note: There is no performance
difference if the type of x_ is some
built-in/intrinsic type, such as int
or char* or float. But even in these
cases, my personal preference is to
set those data members in the
initialization list rather than via
assignment for consistency. Another
symmetry argument in favor of using
initialization lists even for
built-in/intrinsic types: non-static
const and non-static reference data
members can't be assigned a value in
the constructor, so for symmetry it
makes sense to initialize everything
in the initialization list.

Now for the exceptions. Every rule has
exceptions (hmmm; does "every rule has
exceptions" have exceptions? reminds
me of Gödel's Incompleteness
Theorems), and there are a couple of
exceptions to the "use initialization
lists" rule. Bottom line is to use
common sense: if it's cheaper, better,
faster, etc. to not use them, then by
all means, don't use them. This might
happen when your class has two
constructors that need to initialize
the this object's data members in
different orders. Or it might happen
when two data members are
self-referential. Or when a
data-member needs a reference to the
this object, and you want to avoid a
compiler warning about using the this
keyword prior to the { that begins the
constructor's body (when your
particular compiler happens to issue
that particular warning). Or when you
need to do an if/throw test on a
variable (parameter, global, etc.)
prior to using that variable to
initialize one of your this members.
This list is not exhaustive; please
don't write me asking me to add
another "Or when...". The point is
simply this: use common sense.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文