【问题标题】:reinterpret_cast and dynamic cast questionsreinterpret_cast 和动态转换问题
【发布时间】:2015-12-14 05:38:22
【问题描述】:

我读过关于重新解释和动态演员表的文章,但我看到了一些我有疑问的例子。 reinterpret_cast:

#include <iostream>

using namespace std;

class A
{
public:
    void a() {
        cout << "a";
    }
};
class B: private A
{
public:
    void a() {
        cout << "b";
    }
};


int main()
{
    A *a = new A();
    B *b = reinterpret_cast<B*>(a);
    B *b2 = new B();
    a = reinterpret_cast<A*>(b2);
    b->a();
    a->a();
    return 0;
}

会打印 ba。 我的解释是 reinterpret_cast 改变了位模式,并且这两种类型都有一个名为 a() 的函数,结果就是这样。 然后我看到了这个:

using namespace std;
class B;
class A
{
private:
    int j = 4;
public:
    A() {}
    A(const B &b) {}
    void a() {
        cout << j << endl;
    }
};
class B
{
private:
    int i = 5;
public:
    B() {};
    B(const A &a) {}
    void a() {
        cout << i << endl;
    }
};


int main()
{
    A *a = new A();
    B *b = reinterpret_cast<B*>(a);
    B *b2 = new B();
    a = reinterpret_cast<A*>(b2);
    b->a();
    a->a();
    return 0;
}

然后打印了 45。我猜它有继承的东西,但我不知道如何或为什么。

关于动态转换:

#include <iostream>

using namespace std;

class A {
    public:
    virtual ~A(){}
};
class B {
    public:
    void a() {
        cout << "B" << endl;
    }

    virtual ~B() {}
};

int main()
{
    A *a = new A();
    dynamic_cast<B*>(a)->a();
    return 0;
} 

这将打印“B”。 但如果我要写:

virtual void a() {
        cout << "B" << endl;
    }

我会遇到分段错误。 为什么我得到了我在这两个例子中得到的结果?

感谢您的所有帮助!

【问题讨论】:

  • 有错误的代码会做你意想不到的事情。为什么你会期望这样的代码“将这个指向 B 的指针并假装它是指向 A 的指针而不应用处理继承所需的任何转换”会做任何明智的事情?为什么你会期望在没有任何可以调用它的对象的情况下调用一个虚函数?做没有意义的事,就会得到没有意义的结果。

标签: c++ c++11 dynamic-cast reinterpret-cast


【解决方案1】:

在第一种情况下,您基本上是在对编译器撒谎,并告诉它假装指向 A 的指针是指向 B 的指针,而无需进行必要的转换。不过没关系,因为函数不是虚函数,所以它只是根据指针类型调用函数。

在第二种情况下,动态转换失败,因为这两种类型不相关。但你仍然调用B::a,只是没有对象。这不会导致任何问题,因为不会尝试访问该对象。

在第三种情况下,动态转换再次失败。但是由于该函数是虚拟的,因此执行它需要访问对象以确定其完全派生的类型。由于没有对象(不存在而不是 B),因此失败。

【讨论】:

  • 谢谢!关于 reinterpret_cast,不确定我明白了。在第一种情况下,这两个类中的第一个函数是否重要?其次,如果我会做 B 类:私人 A,我会得到其他结果。为什么继承很重要? (请注意,第一个和第二个都是 reinterpret_cast 而不是动态的)。关于动态 - 你能再解释一下为什么如果函数声明为虚拟它的问题吗?
  • @TonyD 谢谢,已修复。
【解决方案2】:

指向的对象具有编译时类型和运行时类型。您需要了解每一项如何影响所采取的行动。

当您执行 reinterpret_cast 时,您已更改编译时类型但未更改运行时类型。

当你调用一个非虚函数时,你从哪个类获得这个函数只取决于编译时类型而不是运行时类型。然后该函数假定运行时类型与编译时类型相同或派生自它。在您的第一个示例中,该假设是错误的,这使得行为在理论上未定义。但在实践中,函数实际上并不使用对象,因此对象类型错误不会产生任何后果。

在您的第二个示例中,您仍然在编译时类型的类中调用该函数。您的测试隐藏了这一事实,因此您可能对此感到困惑。但是对象是运行时类型的,所以它的数据是在运行时类型中初始化的。该数据是按位置访问的,而不是按名称访问的。所以i 的使用获得了j 的实际值(通过非常未定义的行为),因为它具有相同的位置。我希望这会让您对调用哪个函数感到困惑。如果你改变了,你可以让这个例子更容易理解:

cout << i << endl;

cout << "i == " << i << endl;

在您的第三个示例中,dynamic_cast 仍然无条件地将编译时类型更改为请求的类型。但是,如果无法从实际对象的运行时类型正确地达到所请求的类型(在您的不相关类的示例中是这样),则指针本身为空。因此,当您随后调用一个实际上不使用该对象的函数时,结果在技术上是未定义的,但实际上作为编译时类型执行。但是当你调用虚函数时,调用本身会使用对象,并且由于对象指针为空,所以会出现段错误。

【讨论】:

  • 非常感谢! "cout
猜你喜欢
  • 2014-07-20
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-06
  • 2020-03-28
相关资源
最近更新 更多