【问题标题】:Why is the copy constructor called for the statement d1=d2+d3 in C++?为什么在 C++ 中为语句 d1=d2+d3 调用复制构造函数?
【发布时间】:2013-01-24 07:15:14
【问题描述】:

我是 C++ 的初学者,我使用的资源表明以下语句 d3 = d1 + d2; 调用以下语句:

  • + 运算符
  • 默认构造函数
  • 复制构造函数
  • 析构函数
  • 赋值运算符
  • 析构函数

我不明白为什么在将结果分配给先前声明的变量时调用复制构造函数以及为什么调用 2 个构造函数。

运营商如下:

date& date::operator=(const date& other)
{
cout << "Date assignment op" << endl;
    if (this!=&other){
        day=other.day;
        month=other.month;
        year=other.year;
    }
    return *this;
}

date date::operator+(const date& other) const
{
    cout << "Date Operator + called" << endl;
    date temp;
    temp.day=day+other.day;
    temp.month=month+other.month;
    temp.year=year+other.year;
    return temp;
}

【问题讨论】:

  • 类型 d3; d3 = d1 + d2。这就是为什么我很难理解它。
  • 你的+运算符重载函数里面有什么?你能发布一下吗?
  • 复制构造函数用于构造d1 + d2的(临时)返回值。
  • @DanielTaylor:欢迎来到 SO。请附上相关代码sn-ps,包括operator=operator+的声明。
  • 谢谢,我已经添加了操作符。

标签: c++ copy-constructor


【解决方案1】:

表达式(d1 + d2) 在从operator+ (return temp) 返回时产生一个临时对象。从“temp”创建临时是通过复制构造函数完成的。然后将其分配给 d3。

【讨论】:

  • 请注意,使用 NRVO/RVO,您可能看不到对复制构造函数的调用。请阅读复制省略 (stackoverflow.com/questions/2581424/…)
  • @DanielTaylor 很有趣,我原以为 命名值优化 会省略复制构造。
  • @DanielTaylor 请参阅here 以获取 RVO(尽管没有 N)的示例。
  • @juanchopanza:可能是 Debug vs Release 构建的情况。
【解决方案2】:

当它计算d1+d2时,结果是一个临时对象。为了将一个对象分配给另一个对象,正在调用复制构造函数。

【讨论】:

  • 措辞令人困惑。将一个对象赋值给另一个对象,真正调用的是赋值运算符。
  • return temp; 的运算符+ 中,正在创建一个临时对象,因为正在调用复制构造函数。这发生在到达分配操作员之前。对不起我的英语。
【解决方案3】:

默认情况下,return'ing 一个对象的值将从源对象复制构造一个临时对象,然后将该临时对象传递给调用者。也就是说,d3 = d1 + d2;这个语句基本上就是这样做的:

date implicit_temp(operator+(d1, d2));
d3 = implicit_temp;

这在底层分解为以下逻辑:

// The + operator 
date temp; // The default constructor
temp.day = d1.day + d2.day;
temp.month = d1.month + d2.month;
temp.year = d1.year + d2.year;
// return temp;
date implicit_temp(temp); // The copy constructor
temp.~date(); // Destructor
// Assignment operator
d3.day = implicit_temp.day;
d3.month = implicit_temp.month;
d3.year = implicit_temp.year;
implicit_temp.~date(); // Destructor

它与您看到的输出顺序相同。

如果编译器支持 RVO(返回值优化),它将能够优化由 return 语句创建的临时 date 对象,方法是传递 d3 具有隐藏参数,因此 return 可以通过= 运算符将temp 变量直接分配给d3,使代码行为就像这样编写:

void date::operator+(const date& other, date& result) const
{
    date temp;
    temp.day=day+other.day;
    temp.month=month+other.month;
    temp.year=year+other.year;
    result = temp;
}

这会在底层分解为这个逻辑:

// The + operator 
date temp; // The default constructor
temp.day = d1.day + d2.day;
temp.month = d1.month + d2.month;
temp.year = d1.year + d2.year;
// return temp; - Assignment operator
d3.day = temp.day;
d3.month = temp.month;
d3.year = temp.year;
temp.~date(); // Destructor

【讨论】:

    【解决方案4】:

    复制构造函数在以下情况下被调用:

    A a=b,
    when you return an object,
    when you pass an object.
    

    您的+ 运算符重载函数返回一个临时对象。它实际上将调用一个复制构造函数。

    【讨论】:

      【解决方案5】:

      不要依赖它

      从技术上讲,您的 operator+ 的块中有一个 temp 变量,该变量必须在其外部传递到一个临时对象中,以便稍后提供给您的 operator=

      换句话说,您的operator= 看到的参数不是operator+ 中的temp,而是它的副本,它必须比+ 块寿命更长,才能让=发生。

      但是,如果有的话:

      • + 只有一个 return 语句(实际上有)
      • + 的所有 return 语句总是返回同一个对象

      编译器可以通过将临时局部变量与赋值中使用的临时变量合并来优化返回时复制,并完全省略副本。

      这是极少数可以同时消除副作用的优化之一(参见 C++03 的 §12.8.15 或 C++11 的 12.8.31)。

      复制构造函数是否被调用,最终取决于编译器和所需的优化级别。

      (感谢juanchopanza 的所有澄清)。

      【讨论】:

      • 即使复制 c-tor 有副作用,也会发生复制省略,不是吗?还是仅在 RVO 中允许?
      • @jrok:作为一种优化,它只有在外部效果保持不变的情况下才会发生。如果copy ctor有副作用,那么省略也会消除副作用,这会改变外部行为。
      • @jrok 你说得对,复制省略 is 允许发生,即使它消除了副作用。这是一种可以改变外部行为的优化。这就是了解它非常重要的原因之一。关于这一点,答案是错误的。关于单个 return 语句点,这也不完全正确。如果所有 return 语句都返回相同的对象,它就可以工作。
      • @juanchopanza:如果-只是为了完整性-您可以指定标准的要点,即即使消除副作用也可以完成,我会解决答案。 (或者更好:如果您发布正确的,我可以删除它。目前这是页面中唯一谈论复制省略的宫殿,因此无法消除)
      • 我认为它在 C++03 标准的 §12.8 中。我现在无法访问副本,但我可以稍后查看。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-14
      • 2021-02-18
      • 2014-01-14
      • 1970-01-01
      相关资源
      最近更新 更多