我想要实现的是创建一个子类对象的超类数组。
在我正在做的这个特定的测试中,我想要有一个动物数组,它有一些狗对象和一些猫对象,同时它们保持自己的属性。
#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次的狗is,第二行显示被调用6次的猫is。(*test-7)->get_ID();是最后一个数字。
看起来构造函数是被正确调用的。然而,我不知道我的指针指向哪里,因为我期望的是91而不是0。
如何获得一个可以从每个元素访问信息的动物数组?例如,
animal** myArray;
{do something}
cout << myArray[2].get_name() << endl << myArray[7].get_ID();并且它输出
Woof! Corgi
91发布于 2020-10-27 12:19:06
关于animal类的一个重要细节是:当多态类型的析构函数被调用,但这些析构函数不是virtual时,多态类型可能会遇到问题。建议您将基类的析构函数设为虚的,即使该类本身实际上并不需要析构函数。在这种情况下,您可以告诉编译器您希望析构函数为virtual,但使用以下命令生成它的默认实现:
virtual ~animal() = default;将以上行添加到animal类的public:部分。这将确保您稍后定义的任何派生类都将自动获得虚拟析构函数。
现在来看你剩下的代码:
p = new dog;到现在为止还好。但接下来是这样:
p++;除了使指针指向无效地址之外,不做任何有用的事情。然后,在下一次迭代中,将执行另一个p = new dog;。您之前分配的dog对象现在将永远丢失。你有所谓的“泄密”。
似乎你期望new以一种将对象一个接一个地放入内存的方式来分配对象。事实并非如此。new将在不可预测的位置分配内存。因此,这是:
*test-7无法工作,因为对象在内存中的布局不是您所期望的方式。相反,您得到的是指向最近分配的对象之前的某个内存位置7“位置”的地址,该地址几乎肯定不指向您希望的animal对象。当你后来取消引用时,你得到了未定义的行为。一旦发生这种情况,你就不能再对结果进行推理了。它们可以是任何东西,从看到打印错误的文本到你的程序崩溃。
如果你需要一个animal指针数组,你应该专门创建一个:
animal* animals[12];这将创建一个包含12个animal指针的名为animals的数组。然后,您可以初始化这些指针:
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。作为第一步,可以用std::unique_ptr<animal>替换原始的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()函数退出时),智能指针将自动为您完成此操作。
第二步,您可以用std::vector或std::array替换animals数组。您选择哪一个取决于您是否希望您的阵列以后能够增长或缩小。如果数组中只需要12个对象,那么std::array可以做到:
array<unique_ptr<animal>, 12> animals;(您需要#include <array>。)
其他一切都不会改变。for循环保持不变。
与普通数组(也称为“内置数组”)相比,std::array是更好的选择,因为它提供了一个.size()成员函数,该函数告诉您数组可以容纳的元素数量。因此,您不必手动跟踪数字12。此外,当您将std::array传递给以animal*为参数的函数时,它不会像普通数组那样衰减为指针。这可以防止一些常见的编码错误。如果您想实际从std::array获取animal*指针,可以使用它的.data()成员函数,该函数返回指向数组第一个元素的指针。
如果您希望数组能够在运行时增大或缩小,而不是具有在编译时设置的固定大小,则可以使用std::vector:
vector<unique_ptr<animal>> animals;(您需要#include <vector>。)
这将创建一个可以存储unique_ptr<animal>类型的元素的空向量。要向其中实际添加元素,可以使用std::vector的.push_back()函数
// 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>());
}您可以使用emplace_back()作为优化,而不是push_back(),但在这种情况下,这无关紧要。这里要记住的关键点是,一旦你将元素推入向量中,它就会自动增长。它将自动完成此操作,而无需您手动分配新元素。这使得编写代码变得更容易,更不容易出错。
一旦向量超出作用域(这里是当main()返回时),向量将自动删除它为存储元素而分配的内存,并且由于这些元素是智能指针,它们反过来将自动删除它们所指向的animal对象。
发布于 2020-10-27 12:14:26
如果您是C++的新手,重要的是要从正确的角度出发,并遵循现代最佳实践,即:
new、delete、new[]和delete[]。unique_ptr,shared_ptr,但不要使用make_函数而不是new )。这样你就不需要担心delete了。请参阅Advantages of using std::make_unique over new operator的
std::vector<T> (如果您有固定大小的集合,则使用std::array<T,N> ),而不是new[]或p** (并且永远不要在C++中直接使用malloc或calloc!)using AnAnimal = std::variant<cat,dog>.来实现
不管怎样,这就是我想出来的。class animal、class dog和class cat代码与您发布的代码相同(位于// #region注释中),但是顶部的#include和using语句不同,main方法也不同。
请注意,我的代码假设您有一个符合C++14语言规范和STL的编译器。您的编译器可能默认使用C++11或更早的版本。std::make_unique和std::move函数需要C++14。
如下所示:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// Containers:
using std::vector;
using std::string;
// Smart pointers:
using std::unique_ptr;
using std::move;
using std::make_unique;
// IO:
using std::cout;
using std::endl;
// #region Original classes
//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;
}
// #endregion
int main()
{
// See https://stackoverflow.com/questions/44434706/unique-pointer-to-vector-and-polymorphism
vector<unique_ptr<animal>> menagerie;
// Add 6 dogs:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<dog>() );
}
// Add 6 cats:
for( int i = 0; i < 6; i++ ) {
menagerie.emplace_back( make_unique<cat>() );
}
// Dump:
for ( auto &animal : menagerie ) {
cout << "Id: " << animal->get_ID() << ", Name: \"" << animal->get_name() << "\"" << endl;
}
return 0;
}https://stackoverflow.com/questions/64548052
复制相似问题