C++ 将void*强制转换为多继承类的超类不会';不要调用正确的方法
我正在将指向一组异构对象的指针列表存储到一个容器中,并使用字符串来标识它们。关于这次手术的安全性,我已经讨论过了,我同意 问题是,当类有多个继承时,我试图将同一指针强制转换为一个或另一个超类,第二个强制转换似乎失败了 以下是一个例子:C++ 将void*强制转换为多继承类的超类不会';不要调用正确的方法,c++,casting,multiple-inheritance,void-pointers,C++,Casting,Multiple Inheritance,Void Pointers,我正在将指向一组异构对象的指针列表存储到一个容器中,并使用字符串来标识它们。关于这次手术的安全性,我已经讨论过了,我同意 问题是,当类有多个继承时,我试图将同一指针强制转换为一个或另一个超类,第二个强制转换似乎失败了 以下是一个例子: #include <iostream> #include <map> using namespace std; class A { public: virtual void hello() = 0; }; class B { pub
#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: Hello World!
我甚至不知道这怎么可能,因为我甚至没有打电话给你好
编辑
在理解了和本页副本中讨论的内容后,我最终得出了以下解决方案:
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;
}
intmain()
{
std::map mymap;
C C;
D;
E;
mymap[“C”]=静态转换(&C);
mymap[“D”]=静态投影(&D);
mymap[“E”]=静态投影(&E);
静态_cast(mymap[“D”])->hello();
静态_cast(mymap[“E”])->再见();
静态_cast(mymap[“C”])->hello();
动态播放(静态播放(mymap[“C”])->再见();
返回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类:公共A{
公众:
虚空();
};
丙类:公共乙类
{
公众:
void hello()覆盖{
std::cout您的代码具有未定义的行为
如果将void*
转换为B*
,计算机无法知道B
子对象的位置(在实际为C
的指向对象中)。您只是假装整个对象(或至少是它的前缀)是一个B
,如果它是一个简单的单一继承,这将是正确的……但你的不是;第一个基数是a
,而不是B
不要像这样用void*
擦除类型。-缺少输出说明问题。这里有UB…@LightnessRacesinOrbit,如前一个问题所述,如果字符串正确并且我确切知道我要强制转换的内容,则操作是安全的。这个问题不同,因为现在有两个超类。我不取消rstand为什么如果我将同一个指针投射到一个或另一个超类,我没有得到正确的函数调用。代码当然比这复杂得多,我也没有找到“定义良好的方法”对于我所遇到的一般性问题,不管怎么说,这超出了本文讨论的范围。也许这有助于直觉:如果你有cc;
,并且你试图打印(A*)和(B*)的地址&c
您将得到两个不同的值,对应于两个子对象。编译器使用正确的偏移量调整指针,因为编译器知道我们正在将c*
转换为A*
和B*
。如果我们通过void*
我们会阻止编译器执行调整——因此UB将C*
转换为void*
,然后需要将其转换回C*
,然后,如果需要,可以再次转换为A*
和B*
。我要补充这个答案,如果您想要维护类继承结构,您可能想要拥有一个超类,它是它们的父类,因此您可以你应该写std::map mymap;
。不要把它当作一个解决方案,这是一个可行的想法。是的,这是一个好主意-为win@VictorHMartin我已经希望通过父类实现该解决方案,但不幸的是,原始类是模板化的,并且没有假设所有的模板参数可以有一个共同的祖先。我刚刚发现,我试图做的似乎是类型擦除,所以我需要使用std::any
。顺便说一句,原始代码是开源的,并且是。
...
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;
}