首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >最佳C++移动构造函数实现实践

最佳C++移动构造函数实现实践
EN

Stack Overflow用户
提问于 2016-04-04 14:26:50
回答 2查看 6.4K关注 0票数 9

我试图理解移动构造函数的实现。我们都知道,如果我们需要管理C++类中的资源,就需要实现规则5 (C++编程)。

微软给了我们一个例子:https://msdn.microsoft.com/en-us/library/dd293665.aspx

这里有一个更好的方法,它使用复制交换来避免代码重复:动态分配对象数组

代码语言:javascript
复制
     // C++11
     A(A&& src) noexcept
         : mSize(0)
         , mArray(NULL)
     {
         // Can we write src.swap(*this);
         // or (*this).swap(src);
         (*this) = std::move(src); // Implements in terms of assignment
     }

在移动构造函数中,直接:

代码语言:javascript
复制
         // Can we write src.swap(*this);
         // or (*this).swap(src);

因为我认为(*this) = std::move(src)更复杂一些。因为如果我们无意中写为(*this) = src,它将调用普通赋值操作符,而不是移动赋值运算符。

除了这个问题,在微软的例子中,他们写了这样的代码:在移动赋值操作符中,我们需要检查自赋值吗?这有可能发生吗?

代码语言:javascript
复制
// Move assignment operator.
MemoryBlock& operator=(MemoryBlock&& other)
{
   std::cout << "In operator=(MemoryBlock&&). length = " 
             << other._length << "." << std::endl;

   if (this != &other)
   {
      // Free the existing resource.
      delete[] _data;

      // Copy the data pointer and its length from the 
      // source object.
      _data = other._data;
      _length = other._length;

      // Release the data pointer from the source object so that
      // the destructor does not free the memory multiple times.
      other._data = nullptr;
      other._length = 0;
   }
   return *this;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-04 14:32:27

一种方法是实现默认构造函数、复制构造函数和swap函数。

然后实现移动构造函数,使用前三个操作复制和移动赋值操作符。

例如:

代码语言:javascript
复制
struct X
{
    X();
    X(X const&);
    void swap(X&) noexcept;

    X(X&& b)
        : X() // delegate to the default constructor
    {
        b.swap(*this);
    }

    // Note that this operator implements both copy and move assignments.
    // It accepts its argument by value, which invokes the appropriate (copy or move) constructor.
    X& operator=(X b) {
        b.swap(*this);
        return *this;
    }
};

如果您在C++98中使用了这个成语,那么一旦添加了移动构造函数,就可以得到移动分配,而无需编写一行代码。

在某些情况下,这个成语可能不是最有效的。。因为复制运算符总是先构造一个临时的,然后与它交换。通过手工编写赋值操作符,可以获得更好的性能。如果有疑问,请检查优化的程序集输出并使用分析器。

票数 9
EN

Stack Overflow用户

发布于 2020-07-07 15:37:28

我也在互联网上寻找实现移动构造函数和移动分配的最佳方法。有几种方法,但都不是完美的。

以下是我迄今为止的调查结果。

下面是我作为示例使用的一个Test类:

代码语言:javascript
复制
class Test {
private:
  std::string name_;
  void*       handle_ = nullptr;

public:
  Test(std::string name)
    : name_(std::move(name))
    , handle_(malloc(128))
  {
  }
    
  ~Test()
  {
    if(handle_) free(handle_);
  } 

  Test(Test&& other) noexcept;             // we are going to implement it
  Test& operator=(Test&& other) noexcept;  // we are going to implement it

  void swap(Test& v) noexcept
  {
    std::swap(this->handle_, v.handle_);
    std::swap(this->name_, v.name_);
  }

private:
  friend void swap(Test& v1, Test& v2) noexcept
  {
    v1.swap(v2);
  }
};

方法1:向前直线‘n’

代码语言:javascript
复制
Test::Test(Test&& other) noexcept
  : handle_(std::exchange(other.handle_, nullptr))
  , name_(std::move(other.name_))
{
}

Test& Test::operator=(Test&& other) noexcept
{
  if(handle_) free(handle_);
      
  handle_ = std::exchange(other.handle_, nullptr);
  name_ = std::move(other.name_);

  return *this;
}

优点

  • 最佳表现

缺点

  • 移动构造函数和移动分配中的代码重复
  • 在移动分配中部分地重复析构函数的代码。

方法2:破坏+建筑

代码语言:javascript
复制
Test::Test(Test&& other) noexcept
  : handle_(std::exchange(other.handle_, nullptr))
  , name_(std::move(other.name_))
{
}

Test& Test::operator=(Test&& other) noexcept
{
  this->~Test();
  new (this) Test(std::move(other));

  return *this;
}

优点

  • 没有代码重复
  • 如果没有虚拟函数,则性能良好。

缺点

  • 虚拟方法表(VMT)初始化两次(如果类具有虚拟函数)
  • 不能在基类中使用。基类必须仅实现移动构造函数。

方法3:复制‘n’跳槽

代码语言:javascript
复制
Test::Test(Test&& other) noexcept
  : handle_(std::exchange(other.handle_, nullptr))
  , name_(std::move(other.name_))
{
}

Test& Test::operator=(Test&& other) noexcept
{
  Test (std::move(other)).swap(*this);
  return *this;
}

或复制和移动操作符2 in-1:

代码语言:javascript
复制
Test& Test::operator=(Test other) noexcept
{
  swap(other, *this);
  return *this;
}

优点

  • 没有代码重复

缺点

  • 创建额外的对象
  • swap函数中交换数据成员时创建的更小的对象
  • 交换函数中的某种代码重复

方法4:通过移动分配移动构造函数

这就是你在MSDN上发现的

代码语言:javascript
复制
Test::Test(Test&& other) noexcept
{
  *this = std::move(other);
}

Test& Test::operator=(Test&& other) noexcept
{
  if(handle_) free(handle_);
      
  handle_ = std::exchange(other.handle_, nullptr);
  name_ = std::move(other.name_);

  return *this;
}

优点

  • 没有代码重复

缺点

  • 不适用于包含非默认可构造数据成员的类。
  • 数据成员在移动构造函数中初始化两次。

链接

更多答案

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36405412

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档