避免对象切片 [英] Avoiding object slicing

查看:101
本文介绍了避免对象切片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在刷新C ++,说实话已经有一段时间了.我做了一个控制台式乒乓球游戏,作为一种复习任务,并获得了一些关于将多态用于我的类的信息,以从基础"GameObject"(具有一些将对象绘制到屏幕上的基本方法)派生而来.

So I am refreshing on C++, and honestly it's been awhile. I made a console pong game as a sort of refresher task and got some input on using polymorphism for my classes to derive from a base "GameObject" (that has some base methods for drawing objects to the screen).

输入的其中一项是(后来我问了)是从基类派生时内存如何工作的.因为我还没有真正做过很多高级C ++.

One of the pieces of input was (and I had subsequently asked about) was how memory worked when deriving from base classes. Since I hadn't really done much advanced C++.

例如,假设我们有一个基类,现在它只有一个"draw"方法(顺便说一句为什么我们要说virtual?),因为所有其他派生对象实际上只共享一个通用方法. ,并且正在绘制:

For instance lets say we have a base class, for now it just has a "draw" method (Btw why do we need to say virtual for it?), since all other derived objects really only share one common method, and that's being drawn:

class GameObject
{
public:

    virtual void Draw( ) = 0;
};

例如,我们还有一个球类:

we also have a ball class for instance:

class Ball : public GameObject

我收到的输入是,在适当的游戏中,这些内容可能会保留在GameObject指针的某种矢量中.像这样的东西:std::vector<GameObject*> _gameObjects;

The input I received is that in a proper game these would probably be kept in some sort of vector of GameObject pointers. Something like this: std::vector<GameObject*> _gameObjects;

(所以是指向GameObjects的指针的向量)(顺便说一句,为什么我们在这里使用指针?为什么不只是纯GameObjects?).我们将使用以下示例实例化这些游戏对象之一:

(So a vector of pointers to GameObjects) (BTW Why would we use pointers here? why not just pure GameObjects?). We would instantiate one of these gameObjects with something like:

_gameObjects.push_back( new Ball( -1, 1, boardWidth / 2, boardHeight / 2 ); ); 

(new返回指向正确对象的指针吗?IIRC).根据我的理解,如果我尝试执行以下操作:

(new returns a pointer to the object correct? IIRC). From my understanding if I tried to do something like:

Ball b;
GameObject g = b;

事情会变得混乱(如此处所示:什么是对象切片?)

That things would get messed up (as seen here: What is object slicing?)

但是...我在执行new Ball( -1, 1, boardWidth / 2, boardHeight / 2 );时不是简单地自己创建派生对象,还是会自动将其分配为GameObject吗?我真的无法弄清楚为什么一个有效而另一个无效.它与通过new而不是例如Ball ball创建对象有关吗?

However...am I not simply creating Derived objects on their own when I do the new Ball( -1, 1, boardWidth / 2, boardHeight / 2 ); or is that automatically assigning it as a GameObject too? I can't really figure out why one works and one doesn't. Does it have to do with creating an object via new vs just Ball ball for example?

很抱歉,如果这个问题没有意义,我只是想了解如何进行此对象切片.

Sorry if the question makes no sense, im just trying to understand how this object slicing would happen.

推荐答案

基本问题是复制对象(在类为引用类型"的语言中,这不是问题,但在C ++中,默认值是通过值,即制作副本). 切片"是指将较大的对象(从A派生的类型B)的值复制到较小的对象(A类型).因为A较小,所以只能复制部分副本.

The fundamental issue is copying an object (which is not an issue in languages where classes are "reference types", but in C++ the default is to pass things by value, i.e. making a copy). "Slicing" means copying the value of a bigger object (of type B, which derives from A) into a smaller object (of type A). Because A is smaller, only a partial copy is made.

创建容器时,其元素是它们自己的完整对象.例如:

When you create a container, its elements are full objects of their own. For example:

std::vector<int> v(3);  // define a vector of 3 integers
int i = 42;
v[0] = i;  // copy 42 into v[0]

v[0]int变量,就像i.

类也会发生同样的事情:

The same thing happens with classes:

class Base { ... };
std::vector<Base> v(3);  // instantiates 3 Base objects
Base x(42);
v[0] = x;

最后一行将x对象的内容复制到v[0]对象中.

The last line copies the contents of the x object into the v[0] object.

如果我们这样更改x的类型:

If we change the type of x like this:

class Derived : public Base { ... };
std::vector<Base> v(3);
Derived x(42, "hello");
v[0] = x;

...然后v[0] = x尝试将Derived对象的内容复制到Base对象中.在这种情况下,发生的情况是Derived中声明的所有成员都将被忽略.仅复制在基类Base中声明的数据成员,因为这是所有v[0]可以容纳的空间.

... then v[0] = x tries to copy the contents of a Derived object into a Base object. What happens in this case is that all members declared in Derived are ignored. Only the data members declared in the base class Base are copied, because that's all v[0] has room for.

指针为您提供避免复制的功能.当你做

What a pointer gives you is the ability to avoid copying. When you do

T x;
T *ptr = &x;

ptr不是x的副本,它只是指向x.

, ptr is not a copy of x, it just points to x.

类似地,您可以

Derived obj;
Base *ptr = &obj;

&objptr具有不同的类型(分别为Derived *Base *),但是C ++仍然允许该代码.因为Derived对象包含Base的所有成员,所以可以让Base指针指向Derived实例.

&obj and ptr have different types (Derived * and Base *, respectively), but C++ allows this code anyway. Because Derived objects contain all members of Base, it's OK to let a Base pointer point at a Derived instance.

这实际上为您提供了obj的简化接口.通过ptr访问时,仅具有在Base中声明的方法.但是因为没有进行复制,所以所有数据(包括Derived特定部分)仍然存在并且可以在内部使用.

What this gives you is essentially a reduced interface to obj. When accessed through ptr, it only has the methods declared in Base. But because no copying was done, all data (including the Derived specific parts) are still there and can be used internally.

virtual一样:通常,当您通过类型为Base的对象调用方法foo时,它将完全调用Base::foo(即Base中定义的方法).即使通过使用方法的不同实现实际指向派生对象的指针进行调用(如上所述),也会发生这种情况:

As for virtual: Normally, when you call a method foo through an object of type Base, it will invoke exactly Base::foo (i.e. the method defined in Base). This happens even if the call is made through a pointer that actually points at a derived object (as described above) with a different implementation of the method:

class Base {
    public:
    void foo() const { std::cout << "hello from Base::foo\n"; }
};

class Derived : public Base {
    public:
    void foo() const { std::cout << "hello from Derived::foo\n"; }
};

Derived obj;
Base *ptr = &obj;
obj.foo();  // calls Derived::foo
ptr->foo();  // calls Base::foo, even though ptr actually points to a Derived object

通过将foo标记为virtual,我们强制方法调用使用对象的实际类型,而不是通过以下方式进行调用的声明的指针类型:

By marking foo as virtual, we force the method call to use the actual type of the object, instead of the declared type of the pointer the call is made through:

class Base {
    public:
    virtual void foo() const { std::cout << "hello from Base::foo\n"; }
};

class Derived : public Base {
    public:
    void foo() const { std::cout << "hello from Derived::foo\n"; }
};

Derived obj;
Base *ptr = &obj;
obj.foo();  // calls Derived::foo
ptr->foo();  // also calls Derived::foo

virtual对普通对象无效,因为那里的声明类型和实际类型始终相同.它仅影响通过指向对象的指针(和引用)进行的方法调用,因为这些对象具有引用其他对象(可能具有不同类型)的能力.

virtual has no effect on normal objects because there the declared type and the actual type are always the same. It only affects method calls made through pointers (and references) to objects, because those have the ability to refer to other objects (of potentially different types).

这是存储指针集合的另一个原因:当您有几个GameObject的不同子类,所有这些子类都实现了自己的自定义draw方法时,您希望代码注意实际的类型.对象,因此在每种情况下都会调用正确的方法.如果draw不是虚拟的,则您的代码将尝试调用不存在的GameObject::draw.视您的编码方式而定,它要么不会首先编译,要么会在运行时中止.

And that is another reason to store a collection of pointers: When you have several different subclasses of GameObject, all of which implement their own custom draw method, you want the code to pay attention to the actual types of the objects, so the right method gets called in each case. If draw weren't virtual, your code would attempt to invoke GameObject::draw, which doesn't exist. Depending on how exactly you code it, this either wouldn't compile in the first place or abort at runtime.

这篇关于避免对象切片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆