我们假设我有一个这样的课程:
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);
将避免复制对象两次,因为在第一个中我只是复制一个引用(一个指针)而不是整个对象,所以对象的唯一副本在赋值中: / p>
this->a = val;
答案 0 :(得分:7)
我是否在堆栈上复制v struct两次?一个用于传递参数,另一个用于函数内的赋值,这是对的吗?
没错。你是通过值传递的,因此必须构造一个新对象(在你的例子中,通过复制构造函数)。
现在,在QString
(以及Qt中的许多其他值类型)的特定情况下,该操作非常便宜,因为QString是implicitly shared(读取:引用计数,写入时复制)。对于其他数据类型,操作可能很昂贵。
通过使用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
因此该对象的唯一副本位于作业
中
由于你的类无论如何都存储了一个副本,那么一个常见的模式(参见Modern Effective C ++进行大讨论)就是坚持使用pass-by-value,并使用参数中的move-assignment:
void setA(QString val) {
this->a = std::move(val);
}
在这种情况下,如果使用临时/右值调用setA
,您可以保存副本(val
然后会进行移动构建,然后再次移动,导致2次移动1个副本+ 1个const-reference方法的移动)。如果使用左值调用setA
,则需要支付1个副本+ 1个移动(而不是1个const-reference方法的副本)。
答案 1 :(得分:1)
由于您使用的是QString
,因此您不会拥有2份副本。复制构造函数具有:
此操作需要恒定时间,因为隐式共享QString。这使得从函数返回QString的速度非常快。如果修改了共享实例,它将被复制(写时复制),并且需要线性时间。
因此,在这种情况下,只有当您将val
分配给this->a
时才能获得副本