【问题标题】:How do I move multiple parameter via Move semantics?如何通过移动语义移动多个参数?
【发布时间】:2015-10-13 08:24:07
【问题描述】:

假设我有一个 Object MeasurementValues,它有 n 个不同的指针(这个例子只显示了指向原始类型的指针,但也可能出现指向其他复杂对象的指针)。

class MesaurementValues {
   private:
     int *measurement_1;
     double *measurement_2;
     long long *measurement_3;
     //..
     float *measurement_n;

     int noPointer;
}

我知道,无论如何,这个例子可能有点做作。我尝试在我的代码中填写Rule of Five

我必须这样做吗

this.measurement_x = old.measurement_x ;// for all x = {1,..,n} ?

【问题讨论】:

  • 首先定义你的析构函数。这迫使您了解您的课程的实际含义。然后让所有构造函数都以析构函数有意义的方式设置对象。 (在大多数类中,析构函数应该是空的,或者该类除了析构函数处理的一件特定事情之外什么都不做。)
  • 移动语义是对复制语义的“优化”。所以,问题是你的复制构造函数和 operator= 是什么样子的。
  • 复制构造函数是做什么的?如果它只是对 "1,..,n" 执行赋值,那么无论如何你都不应该写它。
  • 类是否拥有指针指向的对象?换句话说,正如 KerrekSB 所说,您是否希望析构函数在类被销毁时删除它们?一旦你有了所有权的概念,你就可以更好地理解如何实现五法则。就个人而言,我更喜欢Rule of Zero
  • @Ralf Wickum。当您得出任何结论时,请告诉我们,如果任何答案满足您的问题,请礼貌地标记它。

标签: c++ move-semantics


【解决方案1】:

首先请注意,对于普通旧数据类型 (POD) 成员,移动语义比复制语义没有任何优势,但是当您的类包含其他类对象和/或数组时,它肯定会这样做。当你实现移动语义时,这意味着你有一个“移动构造函数”和/或一个“移动赋值运算符”,在你的类中看起来像这样:

class MesaurementValues { private:
     int *measurement_1;
     double *measurement_2;
     long long *measurement_3;
     //..
     float *measurement_n;

     int noPointer; 

     //a couple different objects
     someObject*       pObj1;
     differentObject*  pObj2;

 public:
  MeasurementValues( MeasurementValues&& move );  //move-constructor
  MeasurementValues& operator= (MeasurementValues&& move); //move-assignment
}

-假设你的类有 POD 数据和类对象,并且 -假设有很多变量需要移动:

MeasurementValues::MeasurementValues( MeasurementValues&& old) {
    //copy plain-old-data over
    measurement_1 = old.measurement_1;
    measurement_2 = old.measurement_2;

    //copy over values of the pointers
    pObj1 = old.pObj1;
    pObj2 = old.pObj2;
}

请记住,正如其他发布者所说,移动语义只有在您的数据成员是其他 可移动 对象或动态分配的内存时才有优势。

编辑:

指针必须变为“无效”,因为它们已被移动。因此,我会将它们设置为 null 以防止出现意外行为:

MeasurementValues::MeasurementValues( MeasurementValues&& old) 
: measurement_1() //null...
  //,...
{
  //Swapping null into old...
  std::swap(measurement_1, old.measurement_1);
  //...
}

【讨论】:

  • “请注意,对于普通旧数据类型 (POD) 成员,移动语义比复制语义没有优势”你是对的,很多人认为移动总是比复制更好是一种误解。
  • 感谢您的编辑:是的,这样做总是一个好主意。在我看来,您使用 pImpl 习语的示例更好,因为您永远不必担心必须将指针归零。
【解决方案2】:
Do I have to
this.measurement_x = old.measurement_x ;// for all x = {1,..,n} ?

我会依赖 pimpl 习惯用法,并使用类似唯一指针的东西。下面是一个详细的例子。请注意,您只需使用 Impl,并依赖其默认值(因为它不包含指针)。

#include <iostream>
#include <memory>

struct Moveable
{
  public:
    Moveable();
    ~Moveable();

    Moveable(const Moveable& m);
    Moveable& operator=(const Moveable& m);

    Moveable(Moveable&& m);
    Moveable& operator=(Moveable&& m);

    int foo() const;


  private:
    struct Impl;
    std::unique_ptr<Impl> pimpl_;
};

//Moveable.cpp
struct Moveable::Impl
{
  Impl(): a(1), b(2), c(3), buffer(){}
  int a, b, c;
  char buffer[10000]; //Make it worth our while...
};

int Moveable::foo() const{ return pimpl_->a+pimpl_->b+pimpl_->c;}

Moveable::Moveable()
: pimpl_(new Impl)
{
  std::cout << "Default " << (void*)this << std::endl;
}

Moveable::~Moveable()
{
  std::cout << "Destruct " << (void*)this << std::endl;
  //automagically...
}

Moveable::Moveable(const Moveable&m)
: pimpl_(new Impl(*m.pimpl_))
{
  std::cout << "Copying " << &m << " to " << (void*)this << std::endl;
}

Moveable& Moveable::operator=(const Moveable& m)
{
  std::cout << "Copy assign " << (void*)&m << " to " << (void*)this << std::endl;
  *pimpl_ = *m.pimpl_; //Relying on their defaults...
}

Moveable::Moveable(Moveable&& m)
: pimpl_(move(m.pimpl_))
{
  std::cout << "Moving " << (void*)&m << " to " << (void*)this << std::endl;
}

Moveable& Moveable::operator=(Moveable&& m)
{
  pimpl_ = move(m.pimpl_);
  std::cout << "Move assigning " << &m << " to " << (void*)this << std::endl;
}

int main() 
{
  Moveable x;
  Moveable y(x); //Copying...
  y = x; //Copying assign

  y = Moveable(); //Default construct, then move assignment, destruct temp
  Moveable z(std::move(y));
  std::cout << "Calling foo " << z.foo() << std::endl;

  return 0;
}

【讨论】:

  • 好例子;我认为 pImpl 绝对是所有 c++ 程序员都应该花时间学习的技术。如果你彻底理解了上面的例子,你应该对左值和右值有一个很好的理解。
猜你喜欢
  • 1970-01-01
  • 2018-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多