1

我最近一直在刷新/更新我对 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 函数将 a 返回Base*给给定索引表示的成员。

我想知道的关键部分是 reinterpret_casts。我知道从Derived Struct::*to转换Base Struct::*通常禁止的,但在这种情况下,我只使用指向成员变量的指针来获取指向对象的指针。(我不会尝试将派生对象复制为基础对象,也不会将基础对象填充到派生对象的内存中。)这在 G++ 上按预期工作,我只是想确定我不是这样做会被任何兼容的编译器咬伤。

4

4 回答 4

2

你不应该在那里使用 reinterpret_cast 。实际上,您不应该在您的目标是可移植性的任何地方提及 reinterpret_cast 的使用。根据定义, reinterpret_cast 是具有特定于平台结果的东西。

为了将指向基址的指针转换为派生类的指针,请使用 dynamic_cast,当指向的对象不是派生类时,它将返回 NULL。如果您绝对确定该类是正确的,那么您可以使用 static_cast。

于 2011-01-03T06:06:12.860 回答
1

使用reinterpret_cast几乎从不便携。最重要的是,指向成员转换的指​​针的唯一Type Derived::*有效用途是隐式转换 from toType Base::*和谨慎使用static_castfrom Type Base::*to 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;

}
于 2011-01-03T21:08:34.847 回答
0

编辑:来自标准的参考。如果我没看错,因为你没有遇到任何一个例外,你所做的事情是未指定的,因此可能适用于任何特定的编译器,也可能不适用于任何特定的编译器。相关成员的类型没有任何例外。

从 5.2.10/9 (reinterpret_cast) 开始:

如果 T1 和 T2 都是函数类型或都是对象类型,则“指向 T1 类型 X 的成员的指针”类型的右值可以显式转换为“指向 T2 类型的 Y 成员的指针”类型的右值。66) null成员指针值 (4.11) 转换为目标类型的空成员指针值。此转换的结果未指定,但以下情况除外:

— 将“指向成员函数的指针”类型的右值转换为指向成员函数类型的不同指针并返回其原始类型会产生指向成员值的原始指针。

— 将“指向 T1 类型 X 的数据成员的指针”类型的右值转换为“指向 T2 类型的 Y 数据成员的指针”类型(其中 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型产生指向成员值的原始指针。

于 2011-01-03T06:28:35.933 回答
-1

C 风格的转换总是比 reinterpret_cast 好。

如果 C 样式转换工作,它是独立的有效平台。

始终避免 reinterpret_cast。

已编辑

我的意思是,使用 reinterpret_cast 你可以指向错误的内存地址,C 风格的转换处理所有与平台相关的问题,例如 ABI、内存对齐、指针大小等。

编辑 受评论员的启发,我已阅读 ISO/IEC 14882:2003 第 5.2.10 节“Reinterpret_cast”。

当然,我的理解是有限的,但我突然想起了为什么我首先讨厌 reinterpret_cast。

我认为, reinterpret_cast 缺乏或对继承层次结构的认识非常有限。

如果强制转换操作数是具有复杂继承层次的类的实例指针(例如 ATL/COM 类),则一个 reinterpret_cast 足以杀死您的进程并产生难以理解的错误。

我们可以使用 C 风格的演员表,但对背后的实际演员表操作有模糊的了解。但我们必须真正了解确切的细节才能安全地使用 reinterpret_cast。

于 2011-01-03T06:37:26.873 回答