C++ 从空*向基*浇铸,是任何衍生的<;I>*
在使用外部库允许我通过C++ 从空*向基*浇铸,是任何衍生的<;I>*,c++,casting,polymorphism,virtual-inheritance,C++,Casting,Polymorphism,Virtual Inheritance,在使用外部库允许我通过void*存储一些用户数据的场景中,我面临的情况是,我必须存储指向不同派生的实例的指针,该实例实际上继承自公共的基类型(在所有实际情况中,都是使用不同的派生类型,而不是单一的模板类型;但这简化了问题) 虽然存在一个类似的问题,但答案假定确切的指向类型是已知的,虽然我的一些使用不透明指针的代码知道指向的对象是什么实际(多态)类型,但有些不知道(它甚至可能不是我的代码);但是指向类型实际上从基继承的谓词在这种情况下总是正确的。在存储指针之前向上转换指针不是一个选项,因为在给定对
void*
存储一些用户数据的场景中,我面临的情况是,我必须存储指向不同派生的
实例的指针,该实例实际上继承自公共的基
类型(在所有实际情况中,都是使用不同的派生类型,而不是单一的模板类型;但这简化了问题)
虽然存在一个类似的问题,但答案假定确切的指向类型是已知的,虽然我的一些使用不透明指针的代码知道指向的对象是什么实际(多态)类型,但有些不知道(它甚至可能不是我的代码);但是指向类型实际上从基继承的谓词在这种情况下总是正确的。在存储指针之前向上转换指针不是一个选项,因为在给定对象实例的用例中,其层次结构中可能存在多个派生的,因此不可能向下转换到右边的Derived
我已经通过以下代码(在Linux x86_64上,GCC 7.1.1)测试了使用RTTI从不透明指针获取类型信息的能力:
#包括
#包括
#包括
结构基{
Base(){printf(“Base位于%p\n”,this);}
虚拟~Base(){}
};
模板
派生结构:公共虚拟基{
派生(){printf(“派生的位于%p\n”,I,this);}
虚~Derived(){}
};
常量字符*demangle(常量字符*mangled){
返回abi::uucxa_demangle(损坏,0,0,空ptr);
}
int main(int,char**){
衍生d1;
衍生d2;
void*unkPtr1=&d1,*unkPtr2=&d2;
printf(“unkPtr1->%s\n”,demangle(typeid(*reinterpret_cast(unkPtr1)).name());
printf(“unkPtr2->%s\n”,demangle(typeid(*reinterpret_cast(unkPtr2)).name());
返回0;
}
我不确定在这种情况下重新解释是否安全,但它对我有效™ 在typeid
表达式上
可能的输出是
Base is at 0x7ffff9ee4478
Derived<1> is at 0x7ffff9ee4478
Base is at 0x7ffff9ee4480
Derived<2> is at 0x7ffff9ee4480
unkPtr1 -> Derived<1>
unkPtr2 -> Derived<2>
基准位于0x7ffff9ee4478
派生代码位于0x7ffff9ee4478
基准是0x7ffff9ee4480
派生代码位于0x7ffff9ee4480
unkPtr1->派生
unkPtr2->派生
这表明有足够的类型信息来知道任何指向虚拟基派生类型的指针在继承层次结构中的位置
尝试dynamic\u cast(reinterpret\u cast(unkPtr1))
显然是无效和不安全的(unkPtr1
实际上是一个派生的*
),并且是禁止操作的
是否可以以相对类型安全的方式使用dynamic\u cast
s或RTTI提供的设施,从派生的-指向void*
s获取指向基的指针
这表明有足够的类型信息来知道任何指向虚拟基派生类型的指针在继承层次结构中的位置
它肯定不是。它只证明了编译器的“未定义行为”恰好意味着“按您的期望执行”。它证明了您正在使用的实现为虚拟基类提供了与派生类相同的地址。在这种特殊情况下
<> P> >关于C++标准,您可以将<代码> Value*/Cuff>返回到原来的类型,即当转换为 Value*/Cuff>时(或字节式指针,如<代码>无符号char */COD>)。无论您是否使用重新解释\u cast
、C样式转换或其他类型。如果原始指针是某种类型,则无法将其转换为基类
好的,您可以在那里强制转换它。但是除了将指针强制转换回void*
之外,您不能对指针执行任何操作
在存储指针之前向上转换指针也不是一个选项,因为在我对给定对象实例的用例中,它的层次结构中可能存在多个派生的
,因此不可能向下转换到右侧的派生的
胡说八道。发送代码必须知道类型是什么,因为它正在将其强制转换为void*
。而且,既然它知道类型是什么,它还必须知道基类型是什么。因此,这可能是一个问题的方式是:
如果您有多个基类型,并且发送代码不知道接收代码使用的是哪种基类型
如果发送代码是一个模板,除了需要发送之外,对类型一无所知,那么接收代码需要一个特定的基类
所有这些情况实际上都意味着您编写了不连贯的代码。发送方或接收方中的一方不知道另一方期望的类型。如果发送方和接收方不能就传输类型达成一致,则发送方无法以接收方能理解的方式发送
让你的代码连贯起来,问题就消失了。不幸的是,这不起作用。使用void*
是程序员告诉编译器他们知道底层类型是什么的一种方式,而reinterpret\u cast
是程序员告诉编译器类型是什么的方式。你没有立即回答的唯一原因是没有ticed示例中的错误是因为类没有数据,因此基指针与派生指针相同。数据一出现,事情就不那么协调了。例如,修改示例:
#include <cstdio>
#include <typeinfo>
#include <cxxabi.h>
#include <string>
struct Base {
explicit Base(const int inInt) :
test1(inInt)
{ printf("Base is at %p\n", this); }
virtual ~Base() {}
int test1;
};
template<int I>
struct Derived : public virtual Base {
Derived(const int inInt) :
Base(inInt),
testStr("Derived string")
{ printf("Derived<%i> is at %p\n", I, this); }
virtual ~Derived() {}
std::string testStr;
};
const char* demangle(const char *mangled) {
return abi::__cxa_demangle(mangled, 0, 0, nullptr);
}
int main(int, char**) {
Derived<1> d1(1);
Derived<2> d2(2);
void *unkPtr1 = &d1, *unkPtr2 = &d2;
Base *basePtr1 = reinterpret_cast<Base*>(unkPtr1);
Base *basePtr2 = reinterpret_cast<Base*>(unkPtr2);
Base *realBase1 = &d1;
Base *realBase2 = &d2;
printf("basePtr1 %p, realBase1 %p -> %s\n", basePtr1, realBase1, demangle(typeid(*basePtr1).name()));
printf("basePtr2 %p, realBase2 %p -> %s\n", basePtr2, realBase2, demangle(typeid(*basePtr2).name()));
printf("realBase1 %d\n", realBase1->test1);
printf("realBase2 %d\n", realBase2->test1);
// This may crash, or give bogus values, depending on the exact compiler output.
printf("basePtr1 %d\n", basePtr1->test1);
printf("basePtr2 %d\n", basePtr2->test1);
return 0;
}
#包括
#包括
#包括
#包括
结构基{
显式基数(常量整数):
测试1(inInt)
{printf(“基数在%p\n”,this);}
虚拟~Base(){}
int test1;
};
模板
派生结构:公共虚拟基{
派生(常量整数):
基础(inInt),
testStr(“派生字符串”)
{printf(“派生在%p\n”,I,this);}
虚~Derived(){}
std::string testStr;
};
常量字符*demangle(常量字符*mangled){
返回abi::uucxa_demangle(损坏,0,0,空ptr);
}
int main(int,char**){
Base is at 0x7ffce4892ae8
Derived<1> is at 0x7ffce4892ac0
Base is at 0x7ffce4892aa8
Derived<2> is at 0x7ffce4892a80
basePtr1 0x7ffce4892ac0, realBase1 0x7ffce4892ae8 -> Derived<1>
basePtr2 0x7ffce4892a80, realBase2 0x7ffce4892aa8 -> Derived<2>
realBase1 1
realBase2 2
basePtr1 -460772648
basePtr2 -460772712