C ++移动构造函数,用于拥有指针成员

时间:2017-05-09 18:38:08

标签: c++ move-semantics

我问的东西看起来微不足道,但我遇到了问题。为了便于解释,我们假设这样的结构:

class MyClass{
    int* m_Number;
public:
    int value() const {return *m_Number;}
    void setValue(int val){*m_Number=val;}
    MyClass() : m_Number(new int(3)){}
    ~MyClass() {if(m_Number) delete m_Number;}
    MyClass(const MyClass& other):m_Number(new int(*other.m_Number)){}
    MyClass& operator=(const MyClass& other){*m_Number=*other.m_Number; return *this;}
    MyClass& operator=(MyClass&& other){std::swap(m_Number,other.m_Number); return *this;}
    MyClass(MyClass&& other){
            //????
    }
}

我应该放在那里? 我的选择是:

1)

    MyClass(MyClass&& other)
            :m_Number(other.m_Number)
    {
            other.m_Number=nullptr;
    }

但是从对象移动后不是有效状态。调用value()应该返回一些有效但未确定的东西,而在这里我只是段错误。我可以在value()和setValue()中检查m_Number但你意识到它在实际代码中是一个巨大的拖累。

2)

    MyClass(MyClass&& other)
            :m_Number(other.m_Number)
    {
            other.m_Number= new int(3);
    }

但是一个可以抛出的移动构造函数是不行的(或者至少我理解它)并且它也是性能增强的拖累,实际上这个代码与复制构造函数相同或更差。

你怎么看?

我错过了什么吗?

有没有首选方法?

提前致谢

4 个答案:

答案 0 :(得分:4)

首先,没有理由在这里使用newdelete,您应该使用make_unique<int>来创建对象,使用unique_ptr<int>来自动管理它。但这并没有解决你的问题,移动构造函数可以做什么。除了您建议的两个选项之外,还有其他一些选项:

3)不要给它一个移动构造函数,只需将其保留为可复制的

4)不允许在移动的对象上调用valuesetValue的文档,并使用空指针保留移动的对象。取决于程序中发生的移动可能没什么问题。如果永远不会访问从对象移动的东西,那么一切都可以正常工作。

4a)如上所述,但如果确实发生过,请添加健全性检查:

int value() const {
  assert(m_Number != nullptr);
  return *m_Number;
}

或:

int value() const {
  if (m_Number == nullptr)
    throw std::logic_error("accessed a moved-from object");
  return *m_Number;
}

5)将检查添加到setValue以使用新的int重新初始化对象(如果它当前为空),并使value返回一些默认值:

int value() const { return m_Number ? *m_Number : 0; }

void setValue(int val) {
  if (!m_Number)
    m_Number = new int(val);
  else
    *m_Number = val;
}

答案 1 :(得分:1)

您正在m_Number来电中取消引用您的指针。如果.value()无效,您将始终发生段错误。

您对移动构造函数的第一个解决方案是正确的,您应该将“其他”对象设置为默认状态。要解决此问题,您可以使 ... sheet = ctx.workbook.worksheets.getItem("Sheet1"); sheet.getRange("C:C").insert('right'); await ctx.sync(); let range = sheet.getRange("C4:C7"); range.values = [["foo"],["bar"]]; await ctx.sync(); ... 方法抛出,或者在不存在资源的情况下返回默认值。

您的析构函数已经考虑了空案例,因此请确保其余部分也考虑到它。

答案 2 :(得分:0)

  1. 如果修改MyClass以使m_Number = nullptr是一个有效的状态是合理的,那么你的第一种方法可能是最好的(如果它也是默认状态也是最好的做法)。我认为如果与MyClass关联的无堆内存不是有效状态,则应该在std::unique_ptr内分配它,并将原始指针传递给它而不是实现移动构造

  2. 如果1.不是一个选项,这是一个合理的方法。虽然分配不需要的内存肯定会受到性能影响,但它很小,特别是考虑到你正在构建一个类作为此操作的一部分(它本身需要内存)。如果它是你的例子中需要的一小块内存,内存分配器可能会从它自己的池中获取它,而不需要系统调用。如果它是一大块内存(正如我所期望的那样,因为你正在实现移动语义),那么在大多数现代操作系统上它将是一个惰性分配(因此它仍然比复制构造函数更好)。

答案 3 :(得分:-2)

不要使用原始拥有指针,将您的类转换为使用std::unique_ptr,所有问题都将消失。