初始化和分配

INITIALIZATION和ASSIGNMENT之间的区别究竟是什么?

PS:如果可能,请特别提供C和C ++中的示例。

实际上,我对这些陈述感到困惑……

C ++提供了另一种初始化成员变量的方法,它允许我们在创建成员变量时而不是之后初始化成员变量。 这是通过使用初始化列表完成的。 使用初始化列表与执行隐式赋值非常相似。

天啊。 初始化和分配。 嗯,这肯定是混乱!

初始化是准备使用。 当我们谈论变量时,这意味着给变量一个有用的第一个值 。 一种方法是使用作业。

所以它非常微妙:赋值是进行初始化的一种方法。

赋值适用于初始化例如int ,但它不适用于初始化例如std::string 。 为什么? 因为std::string对象包含至少一个指向动态分配内存的指针,和

  • 如果对象尚未初始化,则需要将该指针设置为指向正确分配的缓冲区(用于保存字符串内容的内存块),但

  • 如果对象已经初始化,则赋值可能必须解除分配旧缓冲区并分配新缓冲区。

因此, std::string对象的赋值运算符显然必须以两种不同的方式运行,具体取决于对象是否已经初始化!

当然,它不会以两种不同的方式表现。 相反,对于std::string对象,初始化由构造函数处理 。 你可以说构造函数的工作是获取将代表对象的内存区域,并将那里的任意位更改为适合于对象类型的东西,这表示有效的对象状态。

对于每个对象,在对象上的任何其他操作之前,理想情况下应该从原始内存初始化初始化。

并且C ++规则有效地保证了这一点。 至少只要您不使用非常低级别的设施。 有人可能会称之为C ++构造保证

所以,这意味着当你这样做

  std::string s( "one" ); 

然后你从原始记忆中做了简单的构造,但是当你做的时候

  std::string s; s = "two"; 

然后你首先构造s (对象状态代表一个空字符串),然后分配给已经初始化的s

最后,这让我可以回答你的问题。 从语言无关编程的角度来看,第一个有用的值可能是已分配的值,因此在该视图中,人们将赋值视为初始化。 然而,在C ++技术层面,通过调用std::string的默认构造函数已经完成了初始化,所以在这个级别,人们认为声明是初始化,而赋值只是稍后的值更改。

所以,特别是术语“初始化”取决于上下文!

简单地运用一些常识来理清别人可能意味着什么。

干杯&hth。,

用最简单的术语来说:

 int a = 0; // initialization of a to 0 a = 1; // assignment of a to 1 

对于内置类型,它相对直接。 对于用户定义的类型,它可能变得更加复杂。 看看这篇文章。

例如:

 class A { public: A() : val_(0) // initializer list, initializes val_ {} A(const int v) : val_(v) // initializes val_ {} A(const A& rhs) : val_(rhs.val_) // still initialization of val_ {} private: int val_; }; // all initialization: A a; A a2(4); A a3(a2); a = a3; // assignment 

初始化是创建具有特定值的实例(类型)。

 int i = 0; 

赋值是为已创建的实例(类型) 赋值

 int i; i = 0 

回答你编辑过的问题:

内部构造函数中的Initializing和Assignment有什么区别?
有什么好处?

使用初始化程序列表初始化成员并在构造函数体内为其分配值之间存在差异。

通过初始化列表初始化字段时,构造函数将被调用一次。

如果使用赋值,则首先使用默认构造函数初始化字段,然后使用实际值重新分配(通过赋值运算符)。

如您所见,在后者中存在创建和分配的额外开销,这对于用户定义的类可能是相当大的。

对于整数数据类型或POD类成员,没有实际开销。


代码示例:

 class Myclass { public: Myclass (unsigned int param) : param_ (param) { } unsigned int param () const { return param_; } private: unsigned int param_; }; 

在上面的例子中:

 Myclass (unsigned int param) : param_ (param) 

此构造在C ++中称为成员初始化列表

它将成员param_ 初始化为值param


你什么时候需要使用成员初始化列表?
如果出现以下情况,您将(非常强制)使用“成员初始化程序”列表:

你的class级有一个参考会员
你的class级有一个const成员或
您的类没有默认构造函数

初始化:给对象一个初始值:

 int a(0); int b = 2; int c = a; int d(c); std::vector e; 

分配:为对象分配新值:

 a = b; b = 5; c = a; d = 2; 

在C中,初始化的一般语法是{}

 struct toto { unsigned a; double c[2] }; struct toto T = { 3, { 4.5, 3.1 } }; struct toto S = { .c = { [1] = 7.0 }, .a = 32 }; 

S的一个被称为“指定的初始化器”,只能从C99开始使用。

  • 省略的字段将自动初始化为相应类型的正确0
  • 此语法甚至适用于基本数据类型,如double r = { 1.0 };
  • 有一个catchall初始值设定项将所有字段设置为0 ,即{ 0 }
  • 如果变量是静态链接,则初始化程序的所有表达式都必须是常量表达式

这个{}语法不能直接用于赋值,但在C99中你可以使用复合文字来代替

  S = (struct toto){ .c = { [1] = 5.0 } }; 

因此,首先在RHS上创建一个临时对象并将其分配给您的对象。

没有人提到的一件事是构造函数中初始化和类字段分配之间的区别。

让我们考虑一下课程:

 class Thing { int num; char c; public: Thing(); }; Thing::Thing() : num(5) { c = 'a'; } 

我们这里有一个构造函数,它将Thing::num初始化为值5,并将’a’赋给Thing::c 。 在这种情况下,差别很小,但正如之前所说的,如果你在本例中用一些任意类替换intchar ,我们将讨论调用参数化构造函数与默认构造函数之后的区别,后跟operator= function 。