这是复制值两次?

假设我有这样的课程:

class Foo { public: QString a; void setA(QString val); } 

并像这样实现setA()

 void Foo::setA(QString val) { this->a = val; } 

并使用它像:

 Foo f; QString v = "foo"; f.setA(v); 

我在堆栈上两次复制v结构? 一个用于传递参数,另一个用于函数内的赋值,对吗? 通过使用void setA(QString &val); 将避免复制对象两次,因为在第一个我只是复制一个引用(一个指针),而不是整个对象,所以该对象的唯一副本是在赋值:

  this->a = val; 

我在堆栈上两次复制v结构? 一个用于传递参数,另一个用于函数内的赋值,对吗?

那就对了。 你传递的价值,因此必须构建一个新的对象(在你的情况下,通过复制构造函数)。

现在,在QString (以及Qt中的许多其他值类型)的特定情况下,由于QString是隐式共享的 (读取:引用计数,写入时复制),该操作非常便宜。 在其他数据类型的情况下,操作可能是昂贵的。

通过使用void setA(QString&val)

或者实际上通过使用setA(const QString &val) ,因为你不会修改val引用的对象。 通过使用const引用,还允许使用临时对象:

 void setA(QString &val); setA(QString("foo")); // does not compile! void setA(const QString &val); setA(QString("foo")); // works 

所以对象的唯一副本就是在赋值中

既然你的类存储了一个副本,那么一个常见的模式(比较现代有效的C + +的大讨论)是坚持传递值,并使用从参数移动分配:

 void setA(QString val) { this->a = std::move(val); } 

正如在这种情况下,你可以保存副本的情况下调用setA与临时/右值( val会得到移动构造,你会再次移动,导致2移动,而不是1副本+1移动常量引用方法)。 在使用左值调用setA情况下,您支付1个副本+ 1个移动(而不是1个常量引用方法副本)。

由于您使用的是QString您将不会拥有2个副本。 复制构造函数有:

此操作需要一段时间,因为QString是隐式共享的。 这使得从一个函数快速返回一个QString。 如果共享实例被修改,它将被复制(写时复制),并且需要线性时间。

因此,在这种情况下,只有当您将val赋值给this->a时,才有副本