将 void* 强制转换为多个继承类的超类不会调用正确的方法

Casting a void* to super classes of multple inherited class doesn't call the right method

本文关键字:超类 调用 方法 继承 void 转换      更新时间:2023-10-16

我将指向一组异构对象的指针列表存储在容器中,并使用字符串来标识它们。关于这次操作的安全性,我已经在这里讨论过了,我对此很好。

问题是,当类具有多重继承并且我尝试将同一指针强制转换为一个或另一个超类时,似乎第二个转换失败了。

下面是一个示例:

#include <iostream>
#include <map>
using namespace std;
class A {
public:
  virtual void hello() = 0;
};
class B {
public:
  virtual void goodbye() = 0;
};
class C : public A,
          public B
{
public:
  void hello() override {
    std::cout << "C says: Hello World!" << std::endl;
  }
  void goodbye() override {
    std::cout << "C says: GoodBye World!" << std::endl;
  }
};
class D : public A {
public:
  void hello() override {
    std::cout << "D says: Hello World!" << std::endl;
  }
};
class E : public B {
public:
  void goodbye() override {
    std::cout << "E says: GoodBye World!" << std::endl;
  }
};
int main()
{
  std::map <std::string, void*> mymap;
  C c;
  D d;
  E e;
  mymap["C"] = (void*) &c;
  mymap["D"] = (void*) &d;
  mymap["E"] = (void*) &e;
  static_cast<A*>(mymap["D"])->hello();
  static_cast<B*>(mymap["E"])->goodbye();
  static_cast<A*>(mymap["C"])->hello();
  static_cast<B*>(mymap["C"])->goodbye();
  return 0;
}

预期输出为:

D says: Hello World!
E says: GoodBye World!
C says: Hello World!
C says: GoodBye World!

但我得到的是:

D says: Hello World!
E says: GoodBye World!
C says: Hello World!
C says: Hello World!
我什至

不知道这怎么可能,因为我什至没有打电话给hello.

编辑在了解了副本和此页面中讨论的内容后,我最终得到了这个解决方案:

int main()
{
  std::map <std::string, void*> mymap;
  C c;
  D d;
  E e;
  mymap["C"] = static_cast<A*>(&c);
  mymap["D"] = static_cast<A*>(&d);
  mymap["E"] = static_cast<B*>(&e);
  static_cast<A*>(mymap["D"])->hello();
  static_cast<B*>(mymap["E"])->goodbye();
  static_cast<A*>(mymap["C"])->hello();
  dynamic_cast<B*>(static_cast<A*>(mymap["C"]))->goodbye();
  return 0;
}

我发现的另一种解决方案是使第二个类继承前一个类:

...
class B : public A{
public:
  virtual void goodbye() = 0;
};
class C : public B
{
public:
  void hello() override {
    std::cout << "C says: Hello World!" << std::endl;
  }
  void goodbye() override {
    std::cout << "C says: GoodBye World!" << std::endl;
  }
};
...
int main()
{
  std::map <std::string, void*> mymap;
  C c;
  D d;
  mymap["C"] = static_cast<A*>(&c);
  mymap["D"] = static_cast<A*>(&d);
  static_cast<A*>(mymap["D"])->hello();
  static_cast<A*>(mymap["C"])->hello();
  dynamic_cast<B*>(static_cast<A*>(mymap["C"]))->goodbye();
  return 0;
}

您的代码具有未定义的行为。

给定一个转换为B* void*,计算机没有办法知道B子对象在哪里(在指向的东西中,实际上是一个C(。你只是假装整个事情(或者,至少,它的前缀(是一个B,如果它是一个简单的单一继承,那将是真的...... 但你的不是;第一个碱基是A,不是B

不要像这样使用void*擦除类型。