C++开发初级


842 浏览 5 years, 3 months

3.4 复制以及赋值的区别

版权声明: 转载请注明出处 http://www.codingsoho.com/

复制以及赋值的区别

有时很难区分对象什么时候用复制构造函数初始化,什么时候用赋值运算符赋值。基本上来说,类似于声明的情况会使用复制构造函数,类似于赋值语句的情况会使用赋值运算符。考虑下面的代码:

SpreadsheetCell myCell(5);
SpreadsheetCell anotherCell(myCell);

AnotherCell由复制构造函数创建。

SpreadsheetCell aThirdCell = myCell;

aThirdCell也是由复制构造函数创建的,因为这条语句是一个声明。

这行代码不会调用operator=!,这语法可以用另一种方式给出:SpreadsheetCell aThirdCell(myCell);

然而:

anotherCell = myCell; // calls operate= for another cell

在此anotherCell己经被构建,因此编译器会调用operator=。

对象作为返回值

当函数或者方法返回对象时,有时很难确切地看出执行了什么样的复制和赋值。回顾下面的getString()代码:

string SpreadsheetCell::getString() const
{
  return mString;
}

现在考虑下面的代码

SpreadsheetCell myCell2(5);
string s1;
s1 = myCell2.getString();

当getString()返回mString时,编译器实际上调用string复制构造函数创建了一个未命名的临时String对象。当将结果赋给s1时,会调用s1的赋值运算符,这个临时String作为参数。然后,这个临时的String对象被销毁。因此,这行简单的代码调用了复制 构造函数以及赋值运算符(针对两个不同的对象)。

在了解了上面的内容后,考虑下面的代码:

SpreadsheetCell myCell3(5);
string s2 = myCell3.getString();

在此情况下,getString()返回mString的时候创建了一个临时的未命名string对象。但现在s2调用的是复制构造函数,而不是赋值运算符。

C++11提供了移动语义(move semantics),编译器可以使用移动构造函数而不是复制构造函数从getString()返回mString,这样做效率更高。后面将讨论移动语义。

如果您忘记了这些事情发生的顺序,或者忘记了调用了哪个构造函数或者运算符,只要在代码中临时包含辅助输出或者用调试器逐步调试代码,很容易就能找到答案

复制构造函数以及对象成员

您还应该注意在构造函数中赋值以及调用复制构造函数的不同之处。如果某个对象包含了其他对象,编译器生成的复制构造函数会递归调用每个被包含对象的复制构造函数。

当您编写自己的复制构造函数时,可以使用前面所示的ctor-initializer提供相同的语义。如果在ctor-initializer中省略了某个数据成员,在执行构造函数体内的代码之前,编译器将对该成员执行默认的初始化(为对象调用无参构造函数)。这样,在执行构造函数体的时候,所有的数据成员都己经被初始化。

例如,您可以这样编写复制构造函数:

SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src) :
  mString(src.mString)
{
  mValue = src.mValue;
}

然而,当您在复制构造函数的函数体内对数据成员赋值的时候,使用的是赋值运算符,而不是复制构造函数,因为它们已经被初始化,就像前面讲述的那样。

在此示例中,mString使用复制构造函数进行初始化,而mValue使用赋值运算符赋值。