在c++中,什么是对象切片,什么时候发生?
当前回答
好的,在阅读了许多解释对象切片的文章后,我会尝试一下,但不知道它是如何变得有问题的。
可能导致内存损坏的恶性场景如下:
类在多态基类上提供(意外地,可能是编译器生成的)赋值。客户端复制并切片派生类的实例。客户端调用一个虚拟成员函数,该函数访问切片状态。
其他回答
class A
{
int x;
};
class B
{
B( ) : x(1), c('a') { }
int x;
char c;
};
int main( )
{
A a;
B b;
a = b; // b.c == 'a' is "sliced" off
return 0;
}
如果您有一个基类a和一个派生类B,那么您可以执行以下操作。
void wantAnA(A myA)
{
// work with myA
}
B derived;
// work with the object "derived"
wantAnA(derived);
现在,方法wantAnA需要派生的副本。然而,派生的对象不能完全复制,因为类B可以创建不在其基类A中的其他成员变量。
因此,要调用wantAnA,编译器将“切片”派生类的所有其他成员。结果可能是您不想创建的对象,因为
它可能是不完整的,它的行为类似于A对象(B类的所有特殊行为都丢失了)。
切片意味着当子类的对象通过值或从期望基类对象的函数传递或返回时,子类添加的数据将被丢弃。
说明:考虑以下类声明:
class baseclass
{
...
baseclass & operator =(const baseclass&);
baseclass(const baseclass&);
}
void function( )
{
baseclass obj1=m;
obj1=m;
}
由于基类复制函数不知道派生的任何信息,因此只复制派生的基部分。这通常被称为切片。
我看到所有的答案都提到了当数据成员被切片时对象切片发生的情况。这里我举了一个示例,说明这些方法不会被重写:
class A{
public:
virtual void Say(){
std::cout<<"I am A"<<std::endl;
}
};
class B: public A{
public:
void Say() override{
std::cout<<"I am B"<<std::endl;
}
};
int main(){
B b;
A a1;
A a2=b;
b.Say(); // I am B
a1.Say(); // I am A
a2.Say(); // I am A why???
}
B(对象B)从A(对象a1和a2)导出。正如我们所期望的,b和a1调用它们的成员函数。但从多态性的角度来看,我们不期望由b赋值的a2不会被重写。基本上,a2只保存b的A类部分,即C++中的对象切片。
要解决此问题,应使用引用或指针
A& a2=b;
a2.Say(); // I am B
or
A* a2 = &b;
a2->Say(); // I am B
C++中的切片问题源于其对象的值语义,这主要是由于与C结构的兼容性。您需要使用显式引用或指针语法来实现大多数其他语言中的“正常”对象行为,即对象总是通过引用传递。
简单的答案是,通过按值将派生对象分配给基础对象来切片对象,即,剩余对象只是派生对象的一部分。为了保持值语义,切片是一种合理的行为,其用途相对较少,这在大多数其他语言中都不存在。有些人认为它是C++的一个特性,而许多人则认为这是C++的怪癖/错误特性之一。