在没有动态转换或静态向下转换的情况下比较C++中的派生类

aar*_*rkk 9 c++ oop

我正在尝试将公共基类的对象进行比较.当两个对象在类中不同或者对象的特定值不同时,比较应该失败(例如,输出失败字符串).理想情况下,比较以某种方式强制执行,这样新的派生类也必须将比较函数写入其类的成员.这是一个代码示例:

#include <iostream>
#include <string>
#include <vector>

class Vehicle 
{ 
public:
  virtual std::string compareTo(Vehicle* v) = 0;
};

class Bicycle : public Vehicle
{
public:
  Bicycle() { color_ = "red"; }
  std::string compareTo(Vehicle* v) { return "We're different vehicles."; }
  std::string compareTo(Bicycle* b) { return color_.compare(b->color_) ? "We're different bicycles." : "We're the same bicycle."; }

private:
  std::string color_;
};

class Car : public Vehicle
{
public:
  Car() { style_ = "sedan"; }
  std::string compareTo(Vehicle* v) { return "We're different vehicles."; }
  std::string compareTo(Car* c) { return style_.compare(c->style_) ? "We're different cars." : "We're the same car."; }

private:
  std::string style_;
};

int main()
{
  Vehicle* compareFrom = new Bicycle();

  std::vector<Vehicle*> compareTos;
  compareTos.push_back(new Bicycle());
  compareTos.push_back(new Car());

  std::vector<Vehicle*>::iterator it;
  for (it = compareTos.begin(); it != compareTos.end(); ++it)
    std::cout << compareFrom->compareTo(*it) << std::endl;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

目前,输出(你可以在这里看到)说"我们是不同的车辆".我知道这种情况正在发生,因为我正在使用抽象基指针.问题是如何解决它!

我想输出喜欢有就是自行车输出,他们是一样的,因为他们有相同的颜色.自行车和汽车应该输出它们是不同的车辆.不同颜色的自行车和不同风格的汽车也应该输出它们的不同.我觉得必须有一个伟大的模式来解决这个问题,但我陷入了动态投射或不安全的垂头问题.此外,我希望在同一个类的成员之间强制执行比较功能(因此自行车必须能够与其他自行车进行比较).

Mac*_*cke 11

您需要多个调度(即选择基于多个变量动态调用哪个函数,而不仅仅是'this').这是因为你需要以某种方式检查类型,否则编译器将对类型进行静态分析并选择要调用的函数(Vehicle中的虚拟函数).

没办法解决这个问题.dynamic_cast是你的朋友,但你可能想要出于性能(或其他)原因推出自己的RTTI系统.(维基百科的文章显示了一种方式..)

 std::string Bicycle::compareTo(Vehicle* v) { 
    if (Bicycle* b = dynamic_cast<Bicycle*>(v)) {
       return compareTo(b);
    } else {
       return "We're different vehicles."; 
    }
 }
Run Code Online (Sandbox Code Playgroud)

Loki C++库中有一个这种模式的实现,如果你有许多需要比较的类型,它可能会有所帮助.

C++中的语言不支持多个分派,大多数主流语言也不支持.有人建议将它添加到C++ 11中,请参阅此问题Bjarne的论文.我认为它被拒绝是因为(已知和未知)动态链接问题,C++标准遗憾地对此一无所知.


Kon*_*lph 6

您的代码存在一个很大的问题,即它不易扩展(违反开放/封闭原则).但是,您可以将比较委托给基类方法.

此外,如果你想强制执行语义(一件好事),那么你将无法绕过低估,抱歉.

为了使其健壮和可扩展,

  1. 使基本方法纯虚拟
  2. 提供基本方法的实现(是的,这有效!即使它是纯虚拟的),它可以比较对象的类型
  3. 在派生类中,使用基类的实现来测试类型相等性,然后进行实际的逻辑检查.
#include <iostream>
#include <iomanip>
#include <string>
#include <typeinfo>

struct vehicle {
    virtual bool compare_to(vehicle const& other) const = 0;
};

bool vehicle::compare_to(vehicle const& other) const {
    return typeid(*this) == typeid(other);
}

struct car : vehicle {
    std::string color;

    car(std::string const& color) : color(color) { }

    bool compare_to(vehicle const& other) const {
        bool result = vehicle::compare_to(other);
        return result and (color == static_cast<car const&>(other).color);
    }
};

struct bike : vehicle {
    int spokes;

    bike(int spokes) : spokes(spokes) { }

    bool compare_to(vehicle const& other) const {
        bool result = vehicle::compare_to(other);
        return result and (spokes == static_cast<bike const&>(other).spokes);
    }
};

int main() {
    car c1("blue");
    car c2("red");
    bike b1(42);

    std::cout << std::boolalpha;
    std::cout << c1.compare_to(c2) << "\n"
              << c1.compare_to(b1) << "\n"
              << c1.compare_to(c1) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

上面的代码,static_cast是安全的,因为我们已事先确保类型是相同的,因此演员阵容永远不会失败.

请注意,typeid这里的使用完全合法.它甚至不应该是非常低效的,因为没有深度类型的层次结构来行走.但是如果你想提高效率,你可以实现一个简单的自己的机制,它使用基类中的静态表将每个创建的实例映射到类型唯一的数字标识符(例如std::map<vehicle*, type_id>,哪里type_id是普通的旧enum)并执行简单的查找.

......或者dynamic_cast实际使用.