这个复制值是两次吗?

时间:2016-01-29 16:16:03

标签: c++ windows qt c++11 copy

我们假设我有一个这样的课程:

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;

2 个答案:

答案 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时才能获得副本