C++ 对空心铸件的运行时检查*
假设我有一个C++ 对空心铸件的运行时检查*,c++,object,dynamic-cast,typechecking,runtime-type,C++,Object,Dynamic Cast,Typechecking,Runtime Type,假设我有一个void*包含一个指向未知类的指针。我想使用dynamic\u cast对我实际拥有的类类型进行运行时检查。例如: class Foo {}; void* bar = new Foo; 如果我尝试执行dynamic\u cast(bar) “void*”:动态强制转换的表达式类型无效 但是我需要dynamic\u cast,因为在我的实际情况下,我不确定bar实际上是Foo* 我已经读过,解决这个问题的一个方法是为bar可能包含的所有对象创建一个基类,重新解释\u cast指向该
void*
包含一个指向未知类的指针。我想使用dynamic\u cast
对我实际拥有的类类型进行运行时检查。例如:
class Foo {};
void* bar = new Foo;
如果我尝试执行dynamic\u cast(bar)
“void*”:动态强制转换的表达式类型无效
但是我需要dynamic\u cast
,因为在我的实际情况下,我不确定bar
实际上是Foo*
我已经读过,解决这个问题的一个方法是为bar
可能包含的所有对象创建一个基类,重新解释\u cast
指向该基类的指针,然后尝试从指向Foo
的对象指针中执行动态\u cast
这对我来说很困难,因为存储在bar
中的对象并非都在我的控制之下。(因为尝试重新创建Java会让我心痛。)还有其他方法可以做到这一点吗?为了确保dynamic_cast编译并工作,您应该使用虚拟方法创建一个抽象类或接口类
#include <cassert>
class Bar
{
public:
Bar() = default;
virtual ~Bar() = default;
};
class Foo : public Bar
{
public:
Foo() = default;
virtual ~Foo() = default;
};
int main()
{
Bar* bar = new Foo;
Foo* foo = dynamic_cast<Foo*>(bar);
assert(foo != nullptr);
}
#包括
分类栏
{
公众:
Bar()=默认值;
virtual~Bar()=默认值;
};
Foo类:公共酒吧
{
公众:
Foo()=默认值;
virtual~Foo()=默认值;
};
int main()
{
Bar*Bar=新的Foo;
Foo*Foo=动态施法(条形);
断言(foo!=nullptr);
}
据我所知,您需要一个多态对象,但不需要公共基类
对于这一点,已经有一个相当标准的习惯用法-它被称为boost::any
boost::any
携带您的对象和一些类型信息。该接口允许您查询该类型,并尝试将any转换为您要查找的类型
动态强制转换
用于将多态对象强制转换为一个类,该类的对象类型与您试图强制转换为其父对象的对象类型相同
void*
与此完全不同。
使用指向void的指针,您实际上是在剥离所有类型信息
dynamic\u cast
知道有一个基类,可以通过RTTI进行类型检查
当你抛出一个空指针时,你对编译器说:“是的,你知道内存中的这个位置吗?好吧,把它当作这个类型使用。”如果内存无效,就会调用UB
你有三个选择
选项1
使用接口。
嗯,多态基类是执行动态\u cast
的唯一方法。没有别的办法,没有黑客,这是唯一的办法。就这么简单
struct Base { virtual ~Base() = default; };
struct Derived : Base {};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived*>(base);
if (derived) {
// derived is valid here.
}
}
struct Base{virtual~Base()=default;};
派生结构:基{};
// ...
空隙试验(基准*基准){
自动导出=动态_铸造(基础);
if(派生){
//这里的派生表达式有效。
}
}
选项2
用指针标识类型
我使用一种方法为每个类型指定一个唯一的标识符,并使用该标识符验证强制转换。在没有任何RTTI的情况下完成
using type_id_t = void(*)();
template <typename T> void type_id() {}
// now we can use a map or a vector.
std::vector<std::pair<type_id_t, void*>> vec;
template<typename T>
void insertToMyVector(T* obj) {
vec.emplace_back(type_id<T>, obj);
}
template<typename T>
T* getObj(int index) {
auto item = vec[index];
return static_cast<T*>(item.first == &type_id<T> ? item.second : nullptr);
}
// ...
int main () {
auto foo = new Foo;
insertToMyVector(foo);
auto maybeFoo = getObj<Foo>(0);
if (maybeFoo) {
// you have a valid Foo here
}
}
使用类型_id_t=void(*)();
模板无效类型_id(){}
//现在我们可以使用地图或向量。
std::vec;
模板
void insertToMyVector(T*obj){
向量安放(类型id,obj);
}
模板
T*getObj(整数索引){
自动项=向量[索引];
返回静态_cast(item.first==&type_id?item.second:nullptr);
}
// ...
int main(){
自动foo=新foo;
插入式TomyVector(foo);
自动maybeFoo=getObj(0);
如果(maybeFoo){
//你这里有一个有效的Foo
}
}
选项3
为任何类型生成派生类
这是非常有用的,因为它可以容纳任何类型,同时保持类型安全。我看起来像解决方案1,但提供了更多的灵活性。技巧是使用模板为任何类型生成派生类。优点是你可以持有任何类型,但可能会使你复杂一点
struct Base { virtual ~Base() = default; };
template<typename T>
struct Derived : Base {
Derived(T&& obj) : _obj{std::move(obj)} {}
Derived(const T& obj) : _obj{obj} {}
T& get() {
return _obj;
}
const T& get() const {
return _obj;
}
private:
T _obj;
};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived<int>*>(base);
if (derived) {
int i = derived->get();
// derived is valid here, and we can safely access the int
}
}
struct Base{virtual~Base()=default;};
模板
派生结构:基{
派生(T&&obj):_obj{std::move(obj)}{
派生(const T&obj):_obj{obj}{
T&get(){
返回对象;
}
常量T&get()常量{
返回对象;
}
私人:
T_obj;
};
// ...
空隙试验(基准*基准){
自动导出=动态_铸造(基础);
if(派生){
int i=派生->获取();
//在这里派生是有效的,我们可以安全地访问int
}
}
然后你需要一些额外的状态来告诉你它是什么类型。设计一个接口/抽象类并声明该类型的bar。相关:即使添加一个基类,我相信dynamic\u cast
解决方案只有当指针真正指向从该基类派生的东西时才有效;如果你想坚持这么多,你应该让你的接口接受指向基的指针,而不是空指针!好吧,这就是断开连接。我认为“可能存储在bar中的对象不都在我的控制之下”意味着您甚至在调用时都没有控制它(即,不仅类不在您的控制之下,而且强制转换到void*
的调用点也不在您的控制之下)。如果你控制了呼叫点,那么是的,你可以保证你使用包装器。但是我认为你的评论不止这些,对吗?具体来说,Bar
子对象需要是另一个对象的包装器。这在这里起作用,但不能解决OP的问题,因为OP无法控制传递给其函数的某些对象。因此,他不能让它们来自一个共同的对象。1是在问题的链接中提出的,不是一个理想的解决方案。2是第一次提出的,但不是解决我问题的好办法。3是所描述的解决方案,是迄今为止最有前途的。2可以通过使用改进,3可以通过添加强制转换操作符而不是get
:operator T(){return\u obj;}
我宁愿添加operator*
和operator->
而不是转换操作符。或者使转换运算符显式可以