我最近一直在刷新/更新我对C ++的知识,而且学习严格的别名使我有点担心将一种类型的指针转换为另一种类型。我知道以下代码示例在我的编译器上实际运行,但我想确保它符合当前标准:
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*(lookupTable[i]));
}
private:
Member1 member1;
Member2 member2;
static Base Tuple::* const lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = {
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1),
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2)
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}
本质上,这个简单的元组包含一对元素,每个元素都应该来自一个公共基类。 Get函数将Base*
返回给定索引所代表的成员。
我想知道的关键部分是reinterpret_casts。我知道从Derived Struct::*
到Base Struct::*
的转换通常是禁止的,但在这种情况下我只使用指向成员变量的指针来获取指针到对象。 (我不会尝试将派生对象复制为基础对象,也不会将基础对象复制到派生对象的内存中。)这在G ++上按预期工作,我只是想确定我不是任何兼容的编译器都会为此感到厌烦。
答案 0 :(得分:2)
你不应该在那里使用reinterpret_cast。实际上你不应该在你的目标是可移植性的任何地方提到reinterpret_cast的使用。根据定义,reinterpret_cast具有特定于平台的结果。
为了将指向base的指针转换为派生类的指针,使用dynamic_cast,当指向的对象不是派生类时,它将返回NULL。如果您完全确定该类是正确的,那么您可以使用static_cast。
答案 1 :(得分:1)
reinterpret_cast
的使用几乎从不可移植。最重要的是,仅对成员强制转换的指针的有效使用是从Type Derived::*
到Type Base::*
的隐式强制转换,并谨慎使用static_cast
来自Type Base::*
1}}到Type Derived::*
。由于您要更改成员的类型,而不是包含成员的对象的类型,因此这两者都不是。
如何将微小的函数放在该数组中而不是指向成员的指针?以下代码经过测试,应完全可移植。
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*lookupTable[i])();
}
private:
Member1 member1;
Member2 member2;
template <typename MemType, MemType Tuple::*member>
Base& GetMember() { return this->*member; }
typedef Base& (Tuple::*get_member_func)();
static const get_member_func lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
const typename Tuple<Base, Member1, Member2>::get_member_func
Tuple<Base, Member1, Member2>::lookupTable[2] = {
&Tuple::GetMember<Member1, &Tuple::member1>,
&Tuple::GetMember<Member2, &Tuple::member2>
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}
答案 2 :(得分:0)
编辑:参考标准。如果我正确地阅读它,因为你没有遇到任何一个例外,你所做的是未指定的,因此可能或可能不适用于任何特定的编译器。相关成员的类型没有任何例外。
从5.2.10 / 9(reinterpret_cast):
“指向成员的指针”的右值 “T1类型的X”可以明确地表示 转换为类型的右值 “如果指向T2的Y成员的指针” T1和T2都是功能类型或 两种对象类型.66)null成员 指针值(4.11)被转换为 null的成员指针值 目的地类型。结果如此 转换未指定,但在 以下情况:
- 转换 rvalue类型“指向成员的指针 函数“指向不同的指针 成员函数类型并返回其中 原始类型产生原始 指向成员价值的指针。
- 转换 “数据指针”类型的右值 类型为T1的X的成员 “指向Y类型数据成员的指针 T2“(对齐要求 T2没有比那些更严格 T1)并恢复其原始类型 产生指向成员的原始指针 值。
答案 3 :(得分:-1)
C样式演员总是比reinterpret_cast好。
如果C样式投射工作,它是独立的有效平台。
始终避免reinterpret_cast。
<强> EDITED 强>
我的意思是,使用reinterpret_cast你可以指出错误的内存地址,C样式转换处理所有平台相关的问题,如ABI,内存对齐,指针大小等。
<强> EDITED 强> 在评论员的启发下,我阅读了ISO / IEC 14882:2003第5.2.10节“Reinterpret_cast”。
当然,我的理解是有限的,但是让我记住为什么我一开始就讨厌reinterpret_cast。
我认为,reinterpret_cast缺乏或者对继承层次结构的认识非常有限。
如果强制转换操作数是具有复杂继承层次结构的类的实例指针(例如ATL / COM类),则一个reinterpret_cast就足以使用难以理解的错误来终止进程。
我们可以使用C风格演员,模糊背后的实际演员操作知识。但我们必须真正了解确切的细节才能安全地使用reinterpret_cast。