假设我有这样的课程:
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
时,才有副本