我怎么知道基类对象的指针数组中元素的类型,它将为派生类分配内存

how do i know what is the type of an element in an array of pointers to an object of base class, that will allocate memory for derived classes

本文关键字:派生 内存 分配 类型 对象 基类 指针 数组 元素 我怎么知道      更新时间:2023-10-16

PCPrinter是基类Item的派生类,如果我制作一个类型为Item的指针数组,并且我想根据用户的输入为数组分配内存,这样它就可以使数组中的元素成为PCs或Printers或它们的混合。

所有的错误都是一样的:

错误:类"Item"没有成员"getPC_Counter()"answers"getP_Counter()"以及"setCapacity())和"setType()">

很抱歉以一种非常奇怪和复杂的方式问我的问题,但我真的不知道如何正确解释我的问题。无论如何,这是我的代码,我会尽力解释我在评论中无法理解的内容:

#include<iostream>
#include<string>
using namespace std;
class Item {
int ID;
public:
Item() {
ID = 0;
}
Item(int i) {
ID = i;
}
void print() {
cout << ID << endl;
}
int getID() {
return ID;
}
};
class PC :public Item {
static int PCc;
string type;
public:
PC() {
type = "";
PCc++;
}
void setType(string t) {
type = t;
}
void print() {
Item::print();
cout << type << endl;
}
string getType() {
return type;
}
int getPC_Counter() {
return PCc;
}
};
class Printer: public Item {
int capacity;
static int printerc;
public:
Printer() {
capacity = 0;
printerc++;
}
void setCapacity(int c) {
capacity = c;
}
void print() {
Item::print();
cout << capacity << endl;
}
int getCapacity() {
return capacity;
}
int getP_Counter() {
return printerc;
}
};
int PC::PCc = 0;
int Printer::printerc = 0;
int main() {
Item *store[5];
string c, t;
int cap;
cout << "pc or printer?" << endl;
for (int i = 0; i < 5; i++) {
cin >> c;
if (c == "pc") {
store[i] = new PC();
cout << "type?" << endl;
cin >> t;
//here how do i use the current element as an object of type PC?
store[i]->setType(t);
}
if (c == "printer") {
store[i] = new Printer();
cout << "capacity?" << endl;
cin >> cap;
//here how do i use the current element as an object of type Printer?
store[i]->setCapacity(cap);
}
}
//here how do i know if the element is of type printer or pc ? 
//how do i use the getP_counter() and getPC_Counter funcions properly?
cout << "number of printers: " << store[0]->getP_Counter() << endl;
cout << "number of PCs: " << store[0]->getPC_Counter() << endl;
return 0;
}

我认为正确的方法是使getP_CountergetPC_Counter成为静态成员函数。那么就不需要动态调度了,因为这些函数无论如何都只访问静态数据。

...
// make functions static
static int getPC_Counter() {
return PCc;
}
...
//later call static functions
cout << "number of printers: " << Printer::getP_Counter() << 'n';
cout << "number of PCs: " << PC::getPC_Counter() << 'n';

要处理setCapacitysetType,只需通过最初分配给实数类型来延迟擦除类型,并在调用这些函数后将其添加到数组中。

PC* pc = new PC();
cout << "type?" << 'n';
cin >> t;
//here how do i use the current element as an object of type PC?
pc->setType(t);
store[i] = pc;

您需要使用dynamic_cast将基指针向下投射到所需的派生指针。例如,在PC:的情况下

dynamic_cast<PC*>(store[i])->setType(t);

将起作用,因为这将Item*(基础)向下转换为PC*(派生)。

注意,因为这是C++,所以应该分别使用std::unique_ptrstd::vector,而不是原始指针和C样式数组。前者仍然允许动态铸造和多态性。换句话说,使用:

std::vector<std::unique_ptr<Item>> store;

而不是

Item* store[5];

解决该问题的一种常见模式称为双重调度
它遵循一个最小的工作示例:

struct Visitor;
struct Base {
virtual void accept(Visitor &) = 0;
};
struct Derived1: Base {
void accept(Visitor &v) override;
void f() {}
};
struct Derived2: Base {
void accept(Visitor &v) override;
void g() {}
};
struct Visitor {
void visit(Derived1 d) { d.f(); }
void visit(Derived2 d) { d.g(); }
};
void Derived1::accept(Visitor &v) { v.visit(*this); }
void Derived2::accept(Visitor &v) { v.visit(*this); }
void func(Base &b) {
Visitor v;
b.accept(v);
}
int main() {
Derived1 d1;
Derived2 d2;
func(d1);
func(d2);
}

基本思想是,您可以使用一个访问者类,该类被属于您的层次结构的所有类接受
当层次结构中的类接受访问者时,它会将自己提升为正确的类型,并将引用传递给访问者。

它在很大程度上是基于多态性的,并且作为一种解决方案非常具有侵入性,但它不需要使用dynamic_cast或任何其他技术。

问题不清楚。也许是:你为什么得到:

"错误:类"Item"没有成员"getPC_Counter()"并且"getP_Counter()"answers"setCapacity())"以及"setType()">

这些错误不言自明。您只是没有为基本类型"Item"编码这4种方法。


如果您正在寻找多态派生对象,即使用虚拟方法,则您的(基类的)虚拟方法不应关心派生的类型。基类成为派生类的接口定义。

即调用虚拟方法print()将调用派生类型的print()。


如果打印机有PC没有的方法(反之亦然),有两种(或多种)方法可以解决基类(Item)和派生类之间的"不匹配"问题。

我更喜欢为所有可能的派生方法提供基类方法。所以我会将这四个方法添加到基类(都标记为虚拟)中

Item::getPC_Counter()
Item::getP_Counter()
Item::setCapacity()
Item::setType()

我也更喜欢基类方法来处理(可能是错误/警告消息)要求派生执行它不支持的方法的概念。这种缺乏支持的情况是由"把苹果和橙子混合在一起"造成的。(我经常看到这种情况。)

如果setCapacity()对于派生类Foo没有意义,则

// virtual
Item::setCapacity(std::string lbl) 
{ std::cerr << " invalid setCapacity() for " << lbl << std::endl; }

Foo::setCapacity() { Item::setCapacity("Foo"); }

看看Foo中的虚拟方法是如何将信息发送到基类方法的?

对于派生类Bar,它确实有设置的能力:

Bar::setCapacity() {  /* perform Bar requirements */ }

也许更好的方法需要比计算机课上更多的英语能力。这种方法要求您创建一个每个派生类都可以支持的动词。

典型的例子可能是类动物,以及衍生物种。并不是所有的物种都会吠叫,但也许你想考虑的所有物种都会发声(),或者它们会说(),或吃(),好吧,另一端。

动物::vocalize()实际上可以由狗::vocal(){bark();}支持,cat::vocalize(){meow();},其中树皮和meow是特定于物种(或派生类)的方法。

我猜不出你说的printer::setCapacity()或pc::setCapability()是什么意思。你需要定义它,然后想出一个pc和打印机都支持的更"通用"的动词。也许这些办公设备不应该解决这些问题。

查看您的多态教程。仔细规划你的物品。

当你可以改进的时候更新你的问题。

您可以统计或动态地投射该指针以了解其类型示例:

`auto e = dynamic_pointer_cast<Type>(pointer);`

动态转换是昂贵的东西,如果你知道是什么类型的,你可能想使用static_cast

auto e = static_pointer_cast<expecting type>(pointer);

示例:

auto e = static_pointer_cast<Pc>(pointer);

此外,如果您使用的是原始指针,您希望使用static_cast而不是static_pointer_cast