在c++中,什么是对象切片,什么时候发生?
当前回答
1.切片问题的定义
如果D是基类B的派生类,则可以将派生类型的对象分配给base类型的变量(或参数)。
例子
class Pet
{
public:
string name;
};
class Dog : public Pet
{
public:
string breed;
};
int main()
{
Dog dog;
Pet pet;
dog.name = "Tommy";
dog.breed = "Kangal Dog";
pet = dog;
cout << pet.breed; //ERROR
尽管上面的赋值是允许的,但赋值给变量宠物的值将丢失其品种字段。这被称为切片问题。
2.如何解决切片问题
为了解决这个问题,我们使用指向动态变量的指针。
例子
Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed;
在这种情况下,没有动态变量的数据成员或成员函数被ptrD(后代类对象)指向的对象将丢失。此外,如果需要使用函数,则函数必须是虚拟函数。
其他回答
谷歌第三次匹配“C++切片”给了我这篇维基百科文章http://en.wikipedia.org/wiki/Object_slicing这(很热,但前几篇文章定义了问题):http://bytes.com/forum/thread163565.html
因此,当您将子类的对象分配给超级类时。超类对子类中的附加信息一无所知,并且没有空间存储它,因此附加信息被“分割”。
如果这些链接没有提供足够的信息来获得“好答案”,请编辑您的问题,让我们知道您还需要什么。
我刚刚遇到了切片问题,很快就到了这里。所以让我再加上两美分。
让我们来举一个“生产代码”(或类似代码)的例子:
假设我们有一个可以调度动作的东西。例如,控制中心UI。此UI需要获取当前可以调度的事物的列表。因此,我们定义了一个包含分派信息的类。让我们称之为行动。因此,Action有一些成员变量。为了简单起见,我们只有2,即std::string名称和std::function<void()>f。然后它有一个void activate(),它只执行f成员。
因此,UI得到了一个std::vector<Action>。设想一些功能,如:
void push_back(Action toAdd);
现在,我们已经从UI的角度确定了它的外观。到目前为止没有问题。但是另一个从事这个项目的人突然决定,Action对象中有一些特殊的动作需要更多的信息。无论出于什么原因。这也可以通过lambda捕获来解决。此示例并非取自代码1-1。
所以这家伙从《行动》中派生出来,以增添自己的味道。他将自己制作的课程的一个实例传递给push_back,但随后程序就失控了。
那发生了什么?正如您可能已经猜到的:对象已被切片。
实例中的额外信息已经丢失,f现在容易出现未定义的行为。
我希望这个例子能给那些在谈论以某种方式派生的A和B时无法真正想象事情的人带来启发。
当派生类对象被分配给基类对象时,派生类对象的其他属性将从基类对象中切下(丢弃)。
class Base {
int x;
};
class Derived : public Base {
int z;
};
int main()
{
Derived d;
Base b = d; // Object Slicing, z of d is sliced off
}
在C++中,派生类对象可以分配给基类对象,但另一种方式是不可能的。
class Base { int x, y; };
class Derived : public Base { int z, w; };
int main()
{
Derived d;
Base b = d; // Object Slicing, z and w of d are sliced off
}
当派生类对象分配给基类对象时,对象切片发生,派生类对象的其他属性被切片以形成基类对象。
C++中的切片问题源于其对象的值语义,这主要是由于与C结构的兼容性。您需要使用显式引用或指针语法来实现大多数其他语言中的“正常”对象行为,即对象总是通过引用传递。
简单的答案是,通过按值将派生对象分配给基础对象来切片对象,即,剩余对象只是派生对象的一部分。为了保持值语义,切片是一种合理的行为,其用途相对较少,这在大多数其他语言中都不存在。有些人认为它是C++的一个特性,而许多人则认为这是C++的怪癖/错误特性之一。