C++ 如何防止移动切片?
当派生类实例作为r值父引用传递给不知情的方法时,后者可以合法地更改父对象的内容,从而导致与实际对象中存储的任何额外数据不一致。因此,为扩展而设计的类不能依赖于默认的移动语义。考虑一个简单的例子: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 {
#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您几乎不想复制或移动多态对象。它们通常位于堆上,通过(智能)访问指针。对于复制,使用virtualclone
习惯用法;几乎没有理由移动它们。因此,如果您的类有一个虚拟析构函数,那么大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
};