- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
所以我正在刷新 C++,老实说,它已经有一段时间了。我制作了一个控制台乒乓球游戏作为一种复习任务,并获得了一些关于对我的类使用多态性以派生自基本“GameObject”(具有一些将对象绘制到屏幕的基本方法)的输入。
其中一个输入是(我随后询问过)从基类派生时内存是如何工作的。因为我并没有真正做过很多高级 C++。
例如,假设我们有一个基类,现在它只有一个“绘制”方法(顺便说一句,为什么我们需要为它说 virtual
?),因为所有其他派生对象实际上只共享一个公共(public)方法, 正在绘制:
class GameObject
{
public:
virtual void Draw( ) = 0;
};
例如我们还有一个球类:
class Ball : public GameObject
我收到的信息是,在适当的游戏中,这些可能会保存在某种游戏对象指针 vector 中。像这样:std::vector<GameObject*> _gameObjects;
(所以是指向游戏对象的指针 vector )(顺便说一句,为什么我们要在这里使用指针?为什么不只是纯游戏对象?)。我们将使用以下内容实例化这些游戏对象之一:
_gameObjects.push_back( new Ball( -1, 1, boardWidth / 2, boardHeight / 2 ); );
(new
返回指向对象的指针是否正确?IIRC)。根据我的理解,如果我尝试做类似的事情:
Ball b;
GameObject g = b;
事情会变得一团糟(如此处所示:What is object slicing?)
但是...当我执行 new Ball( -1, 1, boardWidth / 2, boardHeight / 2 );
时,我不是简单地自己创建 Derived 对象吗?还是自动将其分配为游戏对象?我无法真正弄清楚为什么一个有效而另一个无效。它与通过 new
创建对象有关吗?对比 Ball ball
例如?
如果这个问题没有意义,我很抱歉,我只是想了解这个对象切片是如何发生的。
最佳答案
基本问题是复制对象(这在类是“引用类型”的语言中不是问题,但在 C++ 中默认是按值传递事物,即制作拷贝)。 “切片”是指将较大对象(B
类型,派生自 A
)的值复制到较小对象(A
类型)中).因为 A
较小,所以只制作了部分拷贝。
当你创建一个容器时,它的元素是它们自己的完整对象。例如:
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
一样。
类也会发生同样的事情:
class Base { ... };
std::vector<Base> v(3); // instantiates 3 Base objects
Base x(42);
v[0] = x;
最后一行将 x
对象的内容复制到 v[0]
对象中。
如果我们像这样改变 x
的类型:
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]
的空间。
指针给你的是避免复制的能力。当你做的时候
T x;
T *ptr = &x;
,ptr
不是x
的拷贝,它只是指向x
。
同样,你可以这样做
Derived obj;
Base *ptr = &obj;
&obj
和 ptr
有不同的类型(分别是 Derived *
和 Base *
),但是 C++ 允许这段代码无论如何。因为 Derived
对象包含 Base
的所有成员,所以可以让 Base
指针指向 Derived
实例。
这给你的本质上是一个简化的 obj
接口(interface)。当通过ptr
访问时,它只有在Base
中声明的方法。但是因为没有进行复制,所以所有数据(包括 Derived
特定部分)仍然存在并且可以在内部使用。
至于virtual
:通常,当你通过Base
类型的对象调用方法foo
时,它会调用恰好 Base::foo
(即在 Base
中定义的方法)。即使调用是通过一个指针进行的,该指针实际上指向派生对象(如上所述)并具有不同的方法实现,也会发生这种情况:
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
,我们强制方法调用使用对象的实际类型,而不是调用所通过的指针的声明类型:
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
对普通对象没有影响,因为声明类型和实际类型始终相同。它只会影响通过指向对象的指针(和引用)进行的方法调用,因为它们能够引用其他对象(可能不同类型)。
这是存储指针集合的另一个原因:当您有几个不同的 GameObject
子类时,它们都实现了自己的自定义 draw
方法,您需要注意对象的实际类型的代码,因此在每种情况下都会调用正确的方法。如果 draw
不是虚拟的,您的代码将尝试调用不存在的 GameObject::draw
。根据您对其编码的精确程度,这要么不会首先编译,要么在运行时中止。
关于c++ - 避免对象切片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56464702/
我们已经有一个使用 AnyEvent 的库。它在内部使用 AnyEvent,并最终返回一个值(同步 - 不使用回调)。有什么方法可以将这个库与 Mojolicious 一起使用吗? 它的作用如下: #
我想从 XSD 文件生成带有 JAXB 的 Java 类。 问题是,我总是得到一些像这样的类(删除了命名空间): public static class Action { @X
我有一个关于 html 输入标签或 primefaces p:input 的问题。为什么光标总是自动跳转到输入字段。我的页面高度很高,因此您需要向下滚动。输入字段位于页面末尾,光标自动跳转(加载)到页
我今天在考虑面向对象设计,我想知道是否应该避免 if 语句。我的想法是,在任何需要 if 语句的情况下,您都可以简单地创建两个实现相同方法的对象。这两个方法实现只是原始 if 语句的两个可能的分支。
String graphNameUsed = graphName.getName(); if (graphType.equals("All") || graphType.equals(
我有一张友谊 table CREATE TABLE IF NOT EXISTS `friendList` ( `id` int(10) NOT NULL, `id_friend` int(10
上下文 Debian 64。Core 2 二人组。 摆弄循环。我使用了同一循环的不同变体,但我希望尽可能避免条件分支。 但是,即使我认为它也很难被击败。 我考虑过 SSE 或位移位,但它仍然需要跳转(
我最近在 Java 中创建了一个方法来获取字符串的排列,但是当字符串太长时它会抛出这个错误:java.lang.OutOfMemoryError: Java heap space我确信该方法是有效的,
我正在使用 (C++) 库,其中需要使用流初始化对象。库提供的示例代码使用此代码: // Declare the input stream HfstInputStream *in = NULL; tr
我有一个 SQL 查询,我在 WHERE 子句中使用子查询。然后我需要再次使用相同的子查询将其与不同的列进行比较。 我假设没有办法在子查询之外访问“emp_education_list li”? 我猜
我了解到在 GUI 线程上不允许进行网络操作。对我来说还可以。但是为什么在 Dialog 按钮点击回调上使用这段代码仍然会产生 NetworkOnMainThreadException ? new T
有没有办法避免在函数重定向中使用 if 和硬编码字符串,想法是接收一个字符串并调用适当的函数,可能使用模板/元编程.. #include #include void account() {
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
我正在开发 C++ Qt 应用程序。为了在应用程序或其连接的设备出现故障时帮助用户,程序导出所有内部设置并将它们存储在一个普通文件(目前为 csv)中。然后将此文件发送到公司(例如通过邮件)。 为避免
我有一组具有公共(public)父类(super class)的 POJO。这些存储在 superclass 类型的二维数组中。现在,我想从数组中获取一个对象并使用子类 的方法。这意味着我必须将它们转
在我的代码中,当 List 为 null 时,我通常使用这种方法来避免 for 语句中的 NullPointerException: if (myList != null && myList.size
我正在尝试避免客户端出现 TIME_WAIT。我连接然后设置 O_NONBLOCK 和 SO_REUSEADDR。我调用 read 直到它返回 0。当 read 返回 0 时,errno 也为 0。我
在不支持异常的语言和/或库中,许多/几乎所有函数都会返回一个值,指示其操作成功或失败 - 最著名的例子可能是 UN*X 系统调用,例如 open( ) 或 chdir(),或一些 libc 函数。 无
我尝试按值提取行。 col1 df$col1[col1 == "A"] [1] "A" NA 当然我只想要“A”。如何避免 R 选择 NA 值?顺便说一句,我认为这种行为非常危险,因为很多人都会陷入
我想将两个向量合并到一个数据集中,并将其与函数 mutate 集成为 5 个新列到现有数据集中。这是我的示例代码: vector1% rowwise()%>% mutate(vector2|>
我是一名优秀的程序员,十分优秀!