【问题标题】:Copy constructor return temporary object复制构造函数返回临时对象
【发布时间】:2016-10-10 06:55:27
【问题描述】:

我正在准备关于复制构造函数的讲座,结果发现:

MyClass myFunction()
{
   MyClass mc;
   return mc;
}

声明说:

如果我们调用 myFunction,那么 C++ 将创建一个新的 myClass 对象 当 myFunction 返回时,它被初始化为 mc 。因此,虽然你的代码 可能表现得好像它正在透明地从内部移动对象 myFunction 到你的代码的其余部分,它实际上是一个临时的 复制。

我认为声明 MyClass mc;将创建对象并且它返回表单函数而不是任何临时对象。 我哪里错了?有人可以详细说明语句以便于理解吗?

【问题讨论】:

  • NRVO(命名返回值优化)省略了复制构造函数(应用时)。
  • 而移动构造函数(如果可用)正是移动。我认为,讲座非常过时,根本不清楚。

标签: c++ copy-constructor


【解决方案1】:

我认为声明 MyClass mc;将创建对象

正确。

它返回表单函数而不是任何临时对象。

我不完全理解你的说法,所以我无法判断你是否出错了,但这就是发生的事情:

mc 是一个局部变量。局部变量在函数退出时被销毁。在它被销毁之前,一个临时对象会被复制初始化。然后从函数返回临时对象。

额外知识1:从C++11开始,如果可能,move对临时对象进行复制初始化。

额外知识2:标准明确允许编译器跳过复制/移动,而是在调用站点构造对象。这种优化称为(命名)返回值优化,是复制省略的一种形式。尽管如此,您不能返回既不能复制也不能移动的对象。这是因为 NRVO 不需要 C++ 的实现。

【讨论】:

  • 您的回答似乎自相矛盾。 “X 发生”。 “允许 X 不发生”。
  • @juanchopanza 我故意避免提及异常,以便更容易解释 OP 所询问的语句。理解复制省略不是理解按值返回的必要条件。但是在了解按值返回之前了解复制省略很容易导致误解。
【解决方案2】:

假设你有这个代码:

#include <iostream>
using namespace std;

class MyClass
{
public:

    int *v;

    MyClass()
    {
        v=new int[5];
        for(int i=0;i<5;i++) v[i]=i;  // x will look like : 0,1,2,3,4
    }

    ~MyClass()
    {
        cout<<"Destructor called! Now v is deallocated! \n";
        delete[] v;
    }
};

MyClass myFunction()
{
   MyClass mc;
   return mc;
}

int main()
{
    MyClass x;

    x=myFunction();

    cout<<x.v[2]<<'\n'; //it may or may not print the right thing

    return 0;
}

如您所见,myFunction 如您所说返回对象mc。但是你也知道mc的析构函数会在mc超出作用域时被调用——mc是在myFunction内部声明的,所以在函数执行后会调用析构函数并释放内存( delete[] v)。内存已被释放,但值仍在内存中!因此,即使x=myFunction(); 可以工作,您也会有一个对象,其中 v 指向的内存已被释放!所以,cout&lt;&lt;x.v[2]&lt;&lt;'\n'; 可能 打印不正确。如果您编译代码,它将可能打印正确的值 (2),因为内存没有被覆盖,但是如果您在 cout 语句之前或在使用您的一段时间后进行更多分配操作系统,您会看到打印的值不正确/崩溃,因为内存将被其他程序覆盖。 v 仍然指向那个内存块,但它不知道那里有什么,因为内存已被释放。

myFunction内部:v -> |0|1| 2 |3|4|...

退出myFunction后:v -> |0|1| 2 |3|4|.....

一些内存分配后:v -> |a|1| b |@|%|3|7|2|1|*|!|......

【讨论】:

  • 先生有两个疑问:函数调用后,内存被析构函数释放。所以 x 有一个指向垃圾的指针。理想情况下,cout 语句应该给出错误内存损坏,如果不是,那么在 x 超出范围后再次调用析构函数,然后给出。其次是这里应用临时对象概念的地方。
  • 先生,我已经在 linux 和 g++ 编译器上执行了上面的代码,它在给出内存错误之后正确打印 cout。我想知道为什么它第一次正确打印,而理论上内存是由删除运算符释放的。
  • 我真的不知道答案,但我认为它与操作系统管理内存的方式有关。在 Windows 上,例如 GCC,我什至可以执行 coutstackoverflow.com/a/1584414/3185860
猜你喜欢
  • 2015-12-05
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
相关资源
最近更新 更多