C++ 如何防止移动切片?

C++ 如何防止移动切片?,c++,c++14,move,object-slicing,C++,C++14,Move,Object Slicing,当派生类实例作为r值父引用传递给不知情的方法时,后者可以合法地更改父对象的内容,从而导致与实际对象中存储的任何额外数据不一致。因此,为扩展而设计的类不能依赖于默认的移动语义。考虑一个简单的例子: #include <memory> #include <utility> #include <iostream> struct Resource { int x; Resource(int x_) : x(x_*x_) { } }; struct A {

当派生类实例作为r值父引用传递给不知情的方法时,后者可以合法地更改父对象的内容,从而导致与实际对象中存储的任何额外数据不一致。因此,为扩展而设计的类不能依赖于默认的移动语义。考虑一个简单的例子:

#include <memory>
#include <utility>
#include <iostream>

struct Resource {
  int x;
  Resource(int x_) : x(x_*x_) { }
};

struct A {
  std::unique_ptr<Resource> ptr;

  A(int x) : ptr{std::make_unique<Resource>(x)} { }
  A(A&& other) = default; // i.e. : ptr(std::move(other.ptr)) { }
  virtual ~A() = default;
  // other elements of the rule of 5 left out for brevity

  virtual int value() {
    return ptr ? ptr->x : 0;
  }
};

struct B : A {
  int cached;

  B(int x) : A(x), cached(A::value()) { }

  int value() override {
    return cached;
  }
  int value_parent() {
    return A::value();
  }
};

int main() {
  B b{5};
  std::cout << "Before: b.value() = " << b.value()
        << " (parent: " << b.value_parent() << ")\n";
  A a = std::move(b);
  std::cout << "After: b.value() = " << b.value()
        << " (parent: " << b.value_parent() << ")\n"; // INCONSISTENT!
}
#包括
#包括
#包括
结构资源{
int x;
资源(intx):x(x*x){
};
结构A{
std::唯一的ptr ptr;
A(intx):ptr{std::make_unique(x)}{
A(A&&other)=默认;//即:ptr(std::move(other.ptr)){}
virtual~A()=默认值;
//为了简洁起见,5法则中的其他元素被遗漏了
虚拟整数值(){
返回ptr?ptr->x:0;
}
};
结构B:A{
int缓存;
B(intx):A(x),缓存的(A::value()){}
int value()重写{
返回缓存;
}
int值_父项(){
返回一个::value();
}
};
int main(){
B{5};

std::cout您几乎不想复制或移动多态对象。它们通常位于堆上,通过(智能)访问指针。对于复制,使用virtual
clone
习惯用法;几乎没有理由移动它们。因此,如果您的类有一个虚拟析构函数,那么大5中的其他四个成员应该是
delete
d(或者,如果您需要它们来实现虚拟
clone

但以一种(大多是假设性的)方式当你确实需要移动一个多态对象,而你只有一个基本指针或引用时,你需要意识到从中移动也是对象公共接口的一部分。因此,它需要让整个对象保持一致的状态,而不仅仅是基本部分。所以你需要确保派生部分知道。尽一切努力。N通常,您希望编写一个专用的“从虚拟移动”函数,并在移动构造函数/赋值中调用它:

class Base {      
  virtual void moved_fom() {} // do nothing for base
  // some stuff
  // members of the big 5
  virtual ~Base() = default; 
  Base (Base&& other) {
      // do the move
      other->moved_from();
  }
  // etc      
}; 

现在,任何派生类都可以正确地对从其脚下拉出的基础部分作出反应。

使基础部分非公共。复制和多态性不要混合,这也是正确的。如果您重视您的理智,任何具有虚拟内容的类都应该是不可复制和不可移动的。如果您需要复制,请使用虚拟克隆习惯用法。@n.m.是只要
B
A
的一个方面,它恰好具有一些进一步的功能,即复制(将
B
的实例传递为
常量A&
)应该没有问题。它只是忽略
B
除了
A
之外提供的任何内容,但不影响对象。或者不影响对象?将
B
的实例作为
常量A和
传递总是可以的,与复制无关。复制涉及复制构造函数或复制赋值。这些应该是
deletee> 你的前提是错误的,父类的公共接口不应该在派生类中引起任何不一致。这样做违反了is-a关系或你想调用它的任何东西。
class Base {      
  virtual void moved_fom() {} // do nothing for base
  // some stuff
  // members of the big 5
  virtual ~Base() = default; 
  Base (Base&& other) {
      // do the move
      other->moved_from();
  }
  // etc      
};