【问题标题】:How do I initialize an element of an array of superclasses with an instance of a subclass in C++?如何使用 C++ 中的子类实例初始化超类数组的元素?
【发布时间】: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&amp;)stackoverflow.com/questions/274626/what-is-object-slicing 通常,如果您想要一个异构数组,您需要将 pointers 存储到基类,或者最好存储一个智能指针,例如std::unique_ptr&lt;Animal&gt; animals[100];animals[0] = std::make_unique&lt;Cat&gt;("Fluffy")。你也可以使用std::variant
  • @jtbandes 这似乎与我尝试做的事情的简单程度不同步,对我来说。为什么我需要指针?我希望我的数据按顺序存储在内存中,对于我的用例,我不需要或不想管理分配和释放。我不明白为什么子类化的概念应该与使用指针还是直接引用紧密结合。
  • 好吧,子类可能有不同的大小:您的 Cat 对象包含一个 name 指针,但 Animal 没有,因此 Cat 不适合仅分配给 Animal 的空间。您将需要由指针提供的间接寻址,或某种联合(确保有足够的空间可用;std::variant 可以做到这一点,或者 std::aligned_union +placement new 用于不包含电池的方法)。如果要存储从同一个基派生的多个类,联合将不允许您使用公共基接口,而指针将允许。
  • C++ 残酷地虐待那些敢于对它做出假设的人。在这种情况下,在编译 Animal 时只说只有 CatDog 作为子类存在。一段时间后,Wombat 被添加到第三方库中。 Animal 无法知道这个新的子类。因为这个Animal只知道Animal。所有派生类都是未知的。
  • 顺便说一句,这也是虚拟析构函数很重要的原因:stackoverflow.com/questions/461203/…

标签: c++ inheritance


【解决方案1】:

这是错误的,因为 animals[0] = Cat("Fluffy") 调用了 Animal::operator=(const Animal&amp;),它忘记了对象的所有特定于 Cat 的部分。可怜的蓬松一直是sliced?

一般来说,如果你想要一个异构数组,你需要存储一个指向基类的指针数组,或者最好是一个智能指针,例如:

std::unique_ptr<Animal> animals[100];
animals[0] = std::make_unique<Cat>("Fluffy");

你也可以使用 std::variant 之类的东西。

这是必要的,因为子类可能有不同的大小。例如,在您的情况下, Cat 包含一个 name 指针,但 Animal 没有,因此 Cat 不适合仅分配给 Animal 的空间。要解决此问题,您将需要由指针提供的 indirection,或某种 union 以确保有足够的内联空间可用。 (std::variant 可以这样做,或者std::aligned_union + 放置new 用于不包含电池的方法)。

如果您要存储从同一个基派生的多个类,联合将不允许您使用公共基接口,而指针将允许,这就是我在这里推荐指针的原因。记得给你的基类一个virtual destructor

另见:

【讨论】:

    【解决方案2】:

    你完全不能按照你想要的方式去做。创建该数组时,您分配的空间正好足以容纳 100 个动物。好吧,猫可能是一个更大的对象。没有空间了。

    与其他一些 cmets 一样,您需要使用不同的数据结构,可能是基于指针的数据结构。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 2019-10-12
      • 2013-04-25
      • 2016-06-13
      • 2012-09-21
      • 2017-01-09
      • 2019-11-18
      相关资源
      最近更新 更多