【问题标题】:Is there a way to call derived class methods when iterating through base class references?遍历基类引用时有没有办法调用派生类方法?
【发布时间】:2018-11-30 14:40:43
【问题描述】:

我有一个基类 Auto_Part 和几个派生类,如 class Batteryclass Engineclass Frame 等。我还有一个派生的孙子 class Car,它继承了所有派生部分类。

我的目标:我有一个 Inventory class 和一个库存对象 map<Auto_Part*, int> 。第一个值是要存储的 Auto_Part 参考,第二个值是库存中有多少可用。 用户应该能够使用库存中可用的零件创建汽车对象。我想遍历此地图并使用适当的访问器方法来获取创建我的汽车对象所需的数据。 p>

这是有问题的功能。我正在尝试使用 MVC 模型,因此它是一个控制器功能。

void Controller::add_car_from_parts(){

//All variables used in all of the parts
std::string type, frame_type, category, color, bolt_pattern, tire_type, speed_rating,load_range, w_frame_type;
std::string name, fuel_type, grip_type, horn_type, display_color, seat_material, seat_warmers, door_material, butterfly_doors;
int part_number, cranking_amps, cold_cranking_amps, voltage,diameter,width,ratio, w_diameter,w_width, num_doors,
length,reserve_capacity_minutes,num_cylinders, steering_wheel_diameter, num_seats, display_length;
double price;
int quantity;

Dialogs::message("You will be shown the parts available in the inventory. Please write down the part number of the part you will add to you vehicle.", "PART NUMBER");

view.view_all_inventory();
part_number = view.pn_prompt();
//quantity = view.quantity_prompt();

for(auto x: inventory.get_inventory()){ //inventory is a map<Auto_Part*,int>

    if (x.first->get_part_number() == part_number){ // If part is in the inventory
        if (x.first->get_type() == "Battery"){// and if the part is a battery, auto_part function available in all auto_part classes including class Auto_Part
            name = x.first->get_name(); // auto_part function, available in all auto_part classes including class Auto_Part
            price = x.first->get_price(); // auto_part function, available in all auto_part classes including class Auto_Part
            cranking_amps = x.first->get_cranking_amps(); // battery function only
            cold_cranking_amps = x.first->get_cold_cranking_amps(); // battery function only
            voltage = x.first->get_voltage(); // battery function only
            reserve_capacity_minutes = x.first->get_reserve_capacity_minutes();  //battery function only
            inventory.remove_parts(x.first,1); // removes part from inventory, second value is quantity removed
        }

    }
}

}

所以x.first-&gt;get_part_number()x.first-&gt;get_type()x.first-&gt;get_name()x.first-&gt;get_price() 工作正常,因为它们在 Auto_Part 类以及所有派生类中,因此在迭代时指向适当的 get 函数. 第一个错误cranking_amps = x.first-&gt;get_cranking_amps() 行,我的编译器说“类 Auto_Part 没有 get_cranking_amps() 函数,只有电池函数。”

我想既然地图是由Auto_Part* 而不仅仅是Auto_Part 对象组成的,它会引用适当的类。正如 x 会随着它在映射中迭代到 Battery 对象或 Engine 对象而改变,这取决于它是什么,因此我可以使用它的独特功能。 如何在遍历这个基类引用列表时调用派生类函数?当然,我不需要在基类中创建每个派生类函数,对吧?这似乎会破坏具有独特功能的派生类的目的。

感谢您的宝贵时间。

【问题讨论】:

  • 除非我的search-fu很弱,否则问题不包含virtual这个词。您将要对get_cranking_amps() 等使用虚拟函数。然后您可以调用base_class_pointer-&gt;get_cranking_amps() 并知道正在调用正确的函数。如果你用电池调用它,它会得到正确的号码。如果你在其他东西上调用它,它可以返回 0-1 或其他对你有意义的值。
  • 我强烈建议不要让Car 继承自BatteryEngine 等等。它可能有这些类型的成员数据,但继承在这里无用
  • 一个糟糕的解决方案是将 dynamic_cast 转换为您认为的类型 - 在不是的情况下处理这种情况。一个更好的解决方案可能是有一个 getproperties,它会返回一个属性/值对的映射,供调用者使用,调用者可以选择他想要的东西。 that 的更好版本将是访问者模式。
  • @Caleth - 绝对正确 - 但我不明白你是如何从给出的代码中得出这个结论的。来自“auto_part”的引擎和电池似乎没问题。
  • @davidbak 我不是在谈论 AutoPart 作为基础。即便如此,我还是很想将库存设为vector&lt;variant&lt;Battery, Engine, Frame, ...&gt;&gt;,而不是vector&lt;auto_part_ptr&gt;

标签: c++


【解决方案1】:

编译器不知道get_type() == "Battery"class Battery 有任何关系。你仍然只有一个Auto_Part*

如果您想使用无法通过Auto_Part* 访问的成员,您必须自己进行类型转换,使用强制转换:

auto superclass = x.first;
if (superclass->get_type() == "Battery") {
        name = superclass->get_name();
        price = superclass->get_price();
        Battery* subclass = static_cast<Battery*>(parent);
        cranking_amps = subclass->get_cranking_amps();
        cold_cranking_amps = subclass->get_cold_cranking_amps();
        voltage = subclass->get_voltage();
        reserve_capacity_minutes = subclass->get_reserve_capacity_minutes();
        inventory.remove_parts(superclass,1);
    }

但是,当使用类型系统时,你甚至不需要get_type() 函数,你可以使用运行时类型检查:

auto superclass = x.first;
auto as_battery = dynamic_cast<Battery*>(superclass);
if (as_battery) {
        name = superclass->get_name();a
        price = superclass->get_price();
        cranking_amps = as_battery->get_cranking_amps();
        cold_cranking_amps = as_battery->get_cold_cranking_amps();
        voltage = as_battery->get_voltage();
        reserve_capacity_minutes = as_battery->get_reserve_capacity_minutes();
        inventory.remove_parts(superclass,1);
    }

如果运行时类型不匹配,dynamic_cast 将评估为空指针。

正如@davidbak 所说,访问者模式旨在以一种更易于维护的方式解决这个问题(尽管前面有更多代码)。

【讨论】:

  • 谢谢,这成功了。动态转换不是首选解决方案是否有原因?看起来很有用。
  • @rbb091020:维护陷阱。当你添加一个新的Auto_Part 的子类时,你需要更新的代码到处都是,编译器也没有帮助。如果你使用访问者模式,你只需要更新一个点(访问者接口的声明),编译器就会(通过错误)引导你到每个有类型依赖逻辑的地方。
猜你喜欢
  • 2014-03-22
  • 2021-10-20
  • 2011-05-11
  • 2019-08-31
  • 2019-05-30
  • 1970-01-01
  • 2016-01-01
  • 2014-01-27
  • 1970-01-01
相关资源
最近更新 更多