【发布时间】:2021-02-09 20:56:07
【问题描述】:
说我有
Animal animals[100];
现在,在运行时,我想将其中一个初始化为Cat 的新实例,Animal 的子类。我该怎么做呢?我想也许我能做到
animals[0] = Cat("Fluffy")
但这似乎不起作用。我对构造函数在 C++ 中的工作方式的理解是分配对象的空间,然后将指向该空间的指针传递给构造函数(如this)。特别是,即使该空间包含任意垃圾数据,构造函数也能正常工作。所以在我看来,即使animals[0] 已经包含由Animal 的构造函数初始化的数据,或者任何其他预先占用该插槽的数据,也应该可以在该空间上调用Cat 的构造函数并拥有它就像它是一个完全“新鲜”的对象一样工作。我怎样才能做到这一点?
例如,下面的代码应该打印“Fluffy”,但它会打印“Anonymous”。
#include <stdio.h>
class Animal
{
public:
virtual char const *get_name() { return "Anonymous"; };
};
class Cat : public Animal
{
char const *name;
public:
Cat(char const *name) { this->name = name; }
char const *get_name() { return name; }
};
Animal animals[100];
int main()
{
animals[0] = Cat("Fluffy");
printf("%s\n", animals[0].get_name());
return 0;
}
【问题讨论】:
-
这是错误的,因为
animals[0] = Cat("Fluffy")调用Animal::operator=(const Animal&)。 stackoverflow.com/questions/274626/what-is-object-slicing 通常,如果您想要一个异构数组,您需要将 pointers 存储到基类,或者最好存储一个智能指针,例如std::unique_ptr<Animal> animals[100];,animals[0] = std::make_unique<Cat>("Fluffy")。你也可以使用std::variant。 -
@jtbandes 这似乎与我尝试做的事情的简单程度不同步,对我来说。为什么我需要指针?我希望我的数据按顺序存储在内存中,对于我的用例,我不需要或不想管理分配和释放。我不明白为什么子类化的概念应该与使用指针还是直接引用紧密结合。
-
好吧,子类可能有不同的大小:您的 Cat 对象包含一个
name指针,但 Animal 没有,因此 Cat 不适合仅分配给 Animal 的空间。您将需要由指针提供的间接寻址,或某种联合(确保有足够的空间可用;std::variant 可以做到这一点,或者 std::aligned_union +placement new 用于不包含电池的方法)。如果要存储从同一个基派生的多个类,联合将不允许您使用公共基接口,而指针将允许。 -
C++ 残酷地虐待那些敢于对它做出假设的人。在这种情况下,在编译
Animal时只说只有Cat和Dog作为子类存在。一段时间后,Wombat被添加到第三方库中。Animal无法知道这个新的子类。因为这个Animal只知道Animal。所有派生类都是未知的。 -
顺便说一句,这也是虚拟析构函数很重要的原因:stackoverflow.com/questions/461203/…
标签: c++ inheritance