什么是对象切片?
在 C++ 中,什么是对象切片以及何时发生?
In C++, what is object slicing and when does it occur?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
在 C++ 中,什么是对象切片以及何时发生?
In C++, what is object slicing and when does it occur?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(17)
“切片”是将派生类的对象分配给基类的实例,从而丢失部分信息 - 其中一些信息被“切片”掉。
例如,
B
类型的对象有两个数据成员:foo
和bar
。那么如果您要这样写:
那么
b
中关于成员bar
的信息就会在a
中丢失。"Slicing" is where you assign an object of a derived class to an instance of a base class, thereby losing part of the information - some of it is "sliced" away.
For example,
So an object of type
B
has two data members,foo
andbar
.Then if you were to write this:
Then the information in
b
about memberbar
is lost ina
.这里的大多数答案都无法解释切片的实际问题是什么。 他们只解释良性的切片情况,而不解释危险的情况。 假设与其他答案一样,您正在处理两个类
A
和B
,其中B
(公开)派生自一个。
在这种情况下,C++ 允许您将
B
的实例传递给A
的赋值运算符(也传递给复制构造函数)。 这是可行的,因为B
的实例可以转换为const A&
,这正是赋值运算符和复制构造函数所期望的参数。良性情况
那里没有发生任何不好的事情 - 您请求一个
A
的实例,它是B
的副本,而这正是您所得到的。 当然,a
不会包含b
的某些成员,但是应该如何呢? 毕竟,它是一个A
,而不是B
,因此它甚至没有听说过这些成员,更不用说能够存储他们。危险的情况
您可能会认为
b2
之后将是b1
的副本。 但是,唉,事实并非如此! 如果您检查它,您会发现b2
是一种弗兰肯斯坦生物,由b1
的一些块(B
继承自的块)组成A
),以及一些b2
块(仅B
包含的块)。 哎哟!发生了什么? 嗯,默认情况下,C++ 并不将赋值运算符视为虚拟运算符。 因此,
a_ref = b1
行将调用A
的赋值运算符,而不是B
的赋值运算符。 这是因为,对于非虚函数,声明(正式:静态)类型(即A&
)决定调用哪个函数,而不是实际(正式:动态)类型(可能是B
,因为a_ref
引用了一个B
的实例)。 现在,A
的赋值运算符显然只知道A
中声明的成员,因此它只会复制这些成员,而将成员添加到B
中> 不变。解决方案
仅分配给对象的一部分通常没有什么意义,但不幸的是,C++ 没有提供内置方法来禁止这种情况。 但是,您可以自己推出。 第一步是使赋值运算符成为虚拟的。 这将保证调用的始终是实际类型的赋值运算符,而不是声明类型的赋值运算符。 第二步是使用
dynamic_cast
来验证分配的对象是否具有兼容的类型。 第三步是在(受保护的!)成员assign()
中进行实际分配,因为B
的assign()
可能会想要使用A
的assign()
来复制A
的成员。请注意,为了纯粹的方便,
B
的operator=
协变地覆盖了返回类型,因为它知道它正在返回B。
Most answers here fail to explain what the actual problem with slicing is. They only explain the benign cases of slicing, not the treacherous ones. Assume, like the other answers, that you're dealing with two classes
A
andB
, whereB
derives (publicly) fromA
.In this situation, C++ lets you pass an instance of
B
toA
's assignment operator (and also to the copy constructor). This works because an instance ofB
can be converted to aconst A&
, which is what assignment operators and copy-constructors expect their arguments to be.The benign case
Nothing bad happens there - you asked for an instance of
A
which is a copy ofB
, and that's exactly what you get. Sure,a
won't contain some ofb
's members, but how should it? It's anA
, after all, not aB
, so it hasn't even heard about these members, let alone would be able to store them.The treacherous case
You might think that
b2
will be a copy ofb1
afterward. But, alas, it's not! If you inspect it, you'll discover thatb2
is a Frankensteinian creature, made from some chunks ofb1
(the chunks thatB
inherits fromA
), and some chunks ofb2
(the chunks that onlyB
contains). Ouch!What happened? Well, C++ by default doesn't treat assignment operators as
virtual
. Thus, the linea_ref = b1
will call the assignment operator ofA
, not that ofB
. This is because, for non-virtual functions, the declared (formally: static) type (which isA&
) determines which function is called, as opposed to the actual (formally: dynamic) type (which would beB
, sincea_ref
references an instance ofB
). Now,A
's assignment operator obviously knows only about the members declared inA
, so it will copy only those, leaving the members added inB
unchanged.A solution
Assigning only to parts of an object usually makes little sense, yet C++, unfortunately, provides no built-in way to forbid this. You can, however, roll your own. The first step is making the assignment operator virtual. This will guarantee that it's always the actual type's assignment operator which is called, not the declared type's. The second step is to use
dynamic_cast
to verify that the assigned object has a compatible type. The third step is to do the actual assignment in a (protected!) memberassign()
, sinceB
'sassign()
will probably want to useA
'sassign()
to copyA
's, members.Note that, for pure convenience,
B
'soperator=
covariantly overrides the return type, since it knows that it's returning an instance ofB
.如果您有一个基类
A
和一个派生类B
,那么您可以执行以下操作。现在,
wantAnA
方法需要driven
的副本。 但是,派生
对象无法完全复制,因为类B
可能会发明不在其基类A
中的其他成员变量。因此,为了调用
wantAnA
,编译器将“切掉”派生类的所有附加成员。 结果可能是您不想创建的对象,因为A
对象(类B
的所有特殊行为都会丢失)。If You have a base class
A
and a derived classB
, then You can do the following.Now the method
wantAnA
needs a copy ofderived
. However, the objectderived
cannot be copied completely, as the classB
could invent additional member variables which are not in its base classA
.Therefore, to call
wantAnA
, the compiler will "slice off" all additional members of the derived class. The result might be an object you did not want to create, becauseA
-object (all special behaviour of the classB
is lost).这些都是很好的答案。 我只想在按值传递对象与按引用传递对象时添加一个执行示例:
输出为:
These are all good answers. I would just like to add an execution example when passing objects by value vs by reference:
The output is:
谷歌中“C++ 切片”的第三次匹配给了我这篇维基百科文章 http://en.wikipedia.org/wiki /Object_slicing 和这个(激烈,但前几篇文章定义了问题):http:// bytes.com/forum/thread163565.html
因此,当您将子类的对象分配给超类时。 超类对子类中的附加信息一无所知,并且没有空间来存储它,因此附加信息被“切掉”。
如果这些链接没有提供足够的信息来获得“好的答案”,请编辑您的问题,让我们知道您还在寻找什么。
Third match in google for "C++ slicing" gives me this Wikipedia article http://en.wikipedia.org/wiki/Object_slicing and this (heated, but the first few posts define the problem) : http://bytes.com/forum/thread163565.html
So it's when you assign an object of a subclass to the super class. The superclass knows nothing of the additional information in the subclass, and hasn't got room to store it, so the additional information gets "sliced off".
If those links don't give enough info for a "good answer" please edit your question to let us know what more you're looking for.
切片问题很严重,因为它可能导致内存损坏,并且很难保证程序不会受到这种情况的影响。 为了从语言中设计它,支持继承的类应该只能通过引用(而不是通过值)访问。 D 编程语言具有这个特性。
考虑类 A 和从 A 派生的类 B。如果 A 部分具有指针 p 和将 p 指向 B 的附加数据的 B 实例,则可能会发生内存损坏。 然后,当附加数据被切掉时,p 就指向垃圾。
The slicing problem is serious because it can result in memory corruption, and it is very difficult to guarantee a program does not suffer from it. To design it out of the language, classes that support inheritance should be accessible by reference only (not by value). The D programming language has this property.
Consider class A, and class B derived from A. Memory corruption can happen if the A part has a pointer p, and a B instance that points p to B's additional data. Then, when the additional data gets sliced off, p is pointing to garbage.
我看到所有答案都提到当数据成员被切片时对象切片发生。 这里我举一个方法不被重写的例子:
B(对象b)派生自A(对象a1和a2)。 b 和 a1 正如我们所期望的,调用它们的成员函数。 但从多态性的角度来看,我们不希望由 b 指定的 a2 不被覆盖。 基本上,a2仅保存b的A类部分,即C++中的对象切片。
为了解决这个问题,应该使用引用或指针
,或者
I see all the answers mention when object slicing happens when data members are sliced. Here I give an example that the methods are not overridden:
B (object b) is derived from A (object a1 and a2). b and a1, as we expect, call their member function. But from polymorphism viewpoint we don’t expect a2, which is assigned by b, to not be overridden. Basically, a2 only saves A-class part of b and that is object slicing in C++.
To solve this problem, a reference or pointer should be used
or
在 C++ 中,可以将派生类对象分配给基类对象,但反之则不行。
当派生类对象被分配给基类对象时,就会发生对象切片,派生类对象的附加属性被切片以形成基类对象。
In C++, a derived class object can be assigned to a base class object, but the other way is not possible.
Object slicing happens when a derived class object is assigned to a base class object, additional attributes of a derived class object are sliced off to form the base class object.
那么...为什么丢失派生信息是不好的呢? ...因为派生类的作者可能已经更改了表示形式,因此删除额外信息会更改对象表示的值。 如果派生类用于缓存对某些操作更有效的表示,但转换回基本表示的成本很高,则可能会发生这种情况。
还认为有人还应该提到你应该做什么来避免切片......
获取 C++ 编码标准、101 条规则指南和最佳实践的副本。 处理切片是#54。
它提出了一种稍微复杂的模式来完全处理这个问题:有一个受保护的复制构造函数、一个受保护的纯虚拟 DoClone 和一个带有断言的公共克隆,该断言将告诉您(进一步的)派生类是否无法正确实现 DoClone。 (Clone 方法对多态对象进行适当的深层复制。)
您还可以将复制构造函数标记为显式基类,以便在需要时允许显式切片。
So ... Why is losing the derived information bad? ... because the author of the derived class may have changed the representation such that slicing off the extra information changes the value being represented by the object. This can happen if the derived class if used to cache a representation that is more efficient for certain operations, but expensive to transform back to the base representation.
Also thought someone should also mention what you should do to avoid slicing...
Get a copy of C++ Coding Standards, 101 rules guidlines, and best practices. Dealing with slicing is #54.
It suggests a somewhat sophisticated pattern to fully deal with the issue: have a protected copy constructor, a protected pure virtual DoClone, and a public Clone with an assert which will tell you if a (further) derived class failed to implement DoClone correctly. (The Clone method makes a proper deep copy of the polymorphic object.)
You can also mark the copy constructor on the base explicit which allows for explicit slicing if it is desired.
C++ 中的切片问题源于其对象的值语义,这主要是由于与 C 结构体的兼容性。 您需要使用显式引用或指针语法来实现大多数其他处理对象的语言中的“正常”对象行为,即对象始终通过引用传递。
简而言之,您可以通过将派生对象按值分配给基础对象来对对象进行切片,即剩余的对象只是派生对象的一部分。 为了保留值语义,切片是一种合理的行为,并且具有相对较少的用途,这在大多数其他语言中都不存在。 有些人认为它是 C++ 的一个特性,而许多人则认为它是 C++ 的怪癖/错误特性之一。
The slicing problem in C++ arises from the value semantics of its objects, which remained mostly due to compatibility with C structs. You need to use explicit reference or pointer syntax to achieve "normal" object behavior found in most other languages that do objects, i.e., objects are always passed around by reference.
The short answers is that you slice the object by assigning a derived object to a base object by value, i.e. the remaining object is only a part of the derived object. In order to preserve value semantics, slicing is a reasonable behavior and has its relatively rare uses, which doesn't exist in most other languages. Some people consider it a feature of C++, while many considered it one of the quirks/misfeatures of C++.
1. 切片问题的定义
如果 D 是基类 B 的派生类,则可以将 Derived 类型的对象分配给 Base 类型的变量(或参数)。
示例
尽管允许上述赋值,但分配给变量 pet 的值会丢失其品种字段。 这称为切片问题。
2. 如何解决切片问题
为了解决这个问题,我们使用指向动态变量的指针。
示例
在这种情况下,动态变量的数据成员或成员函数都没有
ptrD(后代类对象)指向的内容将会丢失。 另外,如果需要使用函数,该函数必须是虚函数。
1. THE DEFINITION OF SLICING PROBLEM
If D is a derived class of the base class B, then you can assign an object of type Derived to a variable (or parameter) of type Base.
EXAMPLE
Although the above assignment is allowed, the value that is assigned to the variable pet loses its breed field. This is called the slicing problem.
2. HOW TO FIX THE SLICING PROBLEM
To defeat the problem, we use pointers to dynamic variables.
EXAMPLE
In this case, none of the data members or member functions of the dynamic variable
being pointed to by ptrD (descendant class object) will be lost. In addition, if you need to use functions, the function must be a virtual function.
在我看来,除了您自己的类和程序的架构/设计不佳之外,切片并不是什么大问题。
如果我将子类对象作为参数传递给方法,该方法采用超类类型的参数,我当然应该意识到这一点并在内部知道,被调用的方法将仅与超类(也称为基类)对象一起使用。
在我看来,只有不合理的期望,即在请求基类的情况下提供子类,会以某种方式导致子类特定的结果,从而导致切片成为问题。 其要么是该方法的使用设计不佳,要么是子类实现不佳。 我猜测这通常是为了权宜之计或性能提升而牺牲良好的 OOP 设计的结果。
It seems to me, that slicing isn't so much a problem other than when your own classes and program are poorly architected/designed.
If I pass a subclass object in as a parameter to a method, which takes a parameter of type superclass, I should certainly be aware of that and know the internally, the called method will be working with the superclass (aka baseclass) object only.
It seems to me only the unreasonable expectation that providing a subclass where a baseclass is requested, would somehow result in subclass specific results, would cause slicing to be a problem. Its either poor design in the use of the method or a poor subclass implementation. I'm guessing its usually the result of sacrificing good OOP design in favor of expediency or performance gains.
好的,在阅读了许多解释对象切片的帖子后,我会尝试一下,但不知道它是如何成为问题的。
可能导致内存损坏的恶性情况如下:
OK, I'll give it a try after reading many posts explaining object slicing but not how it becomes problematic.
The vicious scenario that can result in memory corruption is the following:
切片意味着当子类的对象通过值或从期望基类对象的函数传递或返回时,子类添加的数据将被丢弃。
说明:
考虑以下类声明:
由于基类复制函数不知道有关派生类的任何信息,因此仅复制派生类的基部分。 这通常称为切片。
Slicing means that the data added by a subclass are discarded when an object of the subclass is passed or returned by value or from a function expecting a base class object.
Explanation:
Consider the following class declaration:
As baseclass copy functions don't know anything about the derived only the base part of the derived is copied. This is commonly referred to as slicing.
当派生类对象分配给基类对象时,派生类对象的附加属性将从基类对象中切掉(丢弃)。
when a derived class object is assigned to a base class object, additional attributes of a derived class object are sliced off (discard) form the base class object.
当派生类对象分配给基类对象时,除了基类中不存在的成员外,派生类对象的所有成员都将复制到基类对象。 这些成员被编译器切掉。
这称为对象切片。
这是一个示例:
它将生成:
When a Derived class Object is assigned to Base class Object, all the members of derived class object is copied to base class object except the members which are not present in the base class. These members are Sliced away by the compiler.
This is called Object Slicing.
Here is an Example:
It will generate: