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

Mar*_*rri 2 c++ casting multiple-inheritance void-pointers

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

问题在于,当该类具有多个继承并且我尝试将相同的指针强制转换为一个或另一个超级类时,似乎第二次强制转换失败。

这是一个例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

预期的输出将是:

D says: Hello World!
E says: GoodBye World!
C says: Hello World!
C says: GoodBye World!
Run Code Online (Sandbox Code Playgroud)

但是我得到的是:

D says: Hello World!
E says: GoodBye World!
C says: Hello World!
C says: Hello World!
Run Code Online (Sandbox Code Playgroud)

我什至不知道怎么可能,因为我什至没有打电话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;
}
Run Code Online (Sandbox Code Playgroud)

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

...
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;
}
Run Code Online (Sandbox Code Playgroud)

Lig*_*ica 5

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

如果将void*转换为B*,计算机将无法知道子对象在哪里(实际上是指向CB对象)。您只是在假装整个事物(或至少是它的前缀)是a B,如果它是一个简单的单一继承,那将是正确的……但是您的事实并非如此。第一基础是一个A,而不是一个B

不要用void*这样的方式擦除类型。