gpt4 book ai didi

c++ - 难以理解c++多态性

转载 作者:行者123 更新时间:2023-12-03 07:09:43 32 4
gpt4 key购买 nike

我试图实现的是创建子类对象的父类(super class)数组。
在我正在进行的这个特定测试中,我想要一个动物数组,其中包含一些狗obj和一些猫obj,同时它们保持其属性。

#include <iostream>
using namespace std;

//ANIMAL
class animal
{
protected:
int ID;
string name;
public:
animal(string = "Unknown");
int get_ID() { return ID; }
virtual string get_name() { return name; }
};

animal::animal(string n) { name = n; }

//DOG
class dog : public animal
{
static int newID;
string sound;
public:
dog(string = "Corgi", string = "Woof!");
string get_name() { return sound + " " + name; }
};

int dog::newID = 0;

dog::dog(string n, string s) : animal(n)
{
newID++;
ID = newID;
cout << ID << "\t";
sound = s;
}

//CAT
class cat : public animal
{
static int meowID;
string color;
public:
cat(string = "Munchkin", string = "Calico");
string get_name() { return color + " " + name; }
};

int cat::meowID = 89;

cat::cat(string n, string c) : animal(n)
{
meowID++;
ID = meowID;
cout << ID << "\t";
color = c;
}


//MAIN
int main(int argc, char* argv[])
{
animal** test;
animal* p;
for (int i = 0; i < 6; i++)
{
p = new dog;
p++;
}
cout << "\n";
for (int i = 0; i < 6; i++)
{
p = new cat;
p++;
}
cout << "\n";
test = &p;
cout << (*test-7)->get_ID();
return 0;
}
到目前为止,我了解到的是p不是数组,它通过循环不断指向不同的内存地址。
我无法执行 animal** test = new dog[6];,因为它是无效的初始化。即使这样行​​得通,我也很难级联cat的另一个数组段。
这是我获得的输出:
1       2       3       4       5       6
90 91 92 93 94 95
0
第一行显示被调用6次的狗ID,第二行显示被调用6次的cat ID。 (*test-7)->get_ID();是最后一个数字。
似乎构造函数正在被正确调用。但是,我不知道指针指向何处,因为我期望91不为0。
如何获得可以访问每个元素信息的动物数组?例如,
animal** myArray;
{do something}
cout << myArray[2].get_name() << endl << myArray[7].get_ID();
它输出
Woof! Corgi
91

最佳答案

关于animal类的一个重要细节:多态类型在调用其析构函数时会遇到问题,但那些析构函数不是virtual。即使基类本身实际上不需要析构函数,也建议您将基类的析构函数设为虚拟。在这种情况下,您可以告诉编译器您希望析构函数为virtual,但使用以下方法生成它的默认实现:

virtual ~animal() = default;
public:类的 animal部分中添加以上行。这样可以确保您以后定义的所有派生类将自动获得虚拟析构函数。
现在到您的其余代码:
p = new dog;
到现在为止还挺好。但是然后这样:
p++;
除了使指针指向无效地址外,没有任何其他帮助。然后在下一次迭代中,将执行另一个 p = new dog;。您分配的上一个 dog对象现在将永远丢失。您遇到了所谓的“泄漏”。
似乎您希望 new分配对象的一种方式可以将它们一个接一个地放置在内存中。事实并非如此。 new将在不可预测的位置分配内存。结果,这:
*test-7
由于对象未按预期方式放置在内存中,因此无法正常工作。相反,您得到的是到最近分配的对象之前7个“位置”的某个内存位置的地址,该地址肯定不会指向您希望的 animal对象。当您以后取消引用时,您将获得未定义的行为。一旦发生这种情况,您就无法再对结果进行推理了。从看到打印错误的文本到程序崩溃,它们可以是任何东西。
如果要使用 animal指针数组,则应专门创建一个:
animal* animals[12];
这将创建一个名为 animals的数组,其中包含12个 animal指针。然后可以初始化这些指针:
for (int i = 0; i < 6; i++) {
animals[i] = new dog;
}

cout << "\n";

for (int i = 6; i < 12; i++) {
animals[i] = new cat;
}
然后,只需指定要访问的数组的索引即可:
cout << animals[0]->get_ID() << '\n'; // first animal
cout << animals[6]->get_ID() << '\n'; // seventh animal
完成数组操作后,不要忘记删除对象。由于 animals是一个数组,因此可以使用ranged循环删除其中的所有对象:
for (auto* animal_obj : animals) {
delete animal_obj;
}
但是,所有这些低级代码都很繁琐且容易出错。建议改用库工具为您完成分配和清理工作,例如 std::unique_ptr。第一步,您可以将原始 animal*指针替换为 std::unique_ptr<animal>:
unique_ptr<animal> animals[12];
(不要忘记在源文件中添加 #include <memory>,因为 std::unique_ptr由该库头提供。)
现在,您有了一个智能指针数组,而不是原始指针。您可以使用以下方法初始化该数组:
for (int i = 0; i < 6; i++) {
animals[i] = make_unique<dog>();
}

cout << "\n";

for (int i = 6; i < 12; i++) {
animals[i] = make_unique<cat>();
}
现在,您不需要 delete任何东西。一旦超出范围,智能指针将自动为您执行此操作(在这种情况下,这意味着 animals数组超出范围,这在 main()函数退出时发生)。
第二步,可以将 animals数组替换为 std::vectorstd::array。选择哪一个取决于您是否希望阵列以后能够增长或缩小。如果只需要数组中的12个对象,则 std::array将执行以下操作:
array<unique_ptr<animal>, 12> animals;
(您需要 #include <array>。)
没有其他改变。 for循环保持不变。 std::array比纯数组(也称为“内置数组”)更好,因为它提供了 .size()成员函数,该函数告诉您数组可以容纳的元素数量。因此,您不必手动跟踪数字12。同样,当您将 std::array传递给以 animal*作为参数的函数时,它不会像普通数组那样衰减到指针。这样可以防止一些常见的编码错误。如果您想从 animal*实际获取 std::array指针,则可以使用其 .data()成员函数,该函数返回指向数组第一个元素的指针。
如果希望数组能够在运行时增大或缩小,而不是具有在编译时设置的固定大小,则可以使用 std::vector代替:
vector<unique_ptr<animal>> animals;
(您需要 #include <vector>。)
这将创建一个空 vector ,该空 vector 可以存储 unique_ptr<animal>类型的元素。要实际向其中添加元素,请使用 .push_back()std::vector函数:
// Add 6 dogs.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<dog>());
}
// Add 6 cats.
for (int i = 0; i < 6; i++) {
animals.push_back(make_unique<cat>());
}
可以使用 push_back()代替 emplace_back()进行优化,但是在这种情况下,它并不重要。他们要记住的关键点是,一旦将元素推入 vector , vector 就会自动增长。它会自动执行此操作,而无需您手动分配新元素。这使得编写代码更加容易,并且不易出错。
一旦 vector 超出范围(此处为 main()返回), vector 将自动删除为其分配的用于存储元素的内存,并且由于这些元素是智能指针,因此它们又将自动删除其指向的 animal对象。 。

关于c++ - 难以理解c++多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64548052/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com