【问题标题】:Difficulty in understanding C-style typecasting and dynamic cast难以理解 C 风格的类型转换和动态转换
【发布时间】:2017-02-03 07:22:06
【问题描述】:

我一直在尝试理解 C++ 中的类型转换。在下面的代码中,B类是A类的子类,两者共享多态关系。

#include <iostream>
using namespace std;
class A{
    int a;
    public:
        virtual void sayhello() {cout<<"Hello from A"<<endl;}
};
class B:public A{
    int b;
    public:
        void sayhello(){cout<<"Hello from B"<<endl;}
        void another_func(){cout<<"Hello from B::another_func"<<endl;}
};
int main() {

    A *temp1 = new A;
    //Statement 1
    B *b =(B*) temp1;

    //Statement 2
    //B* b = dynamic_cast<B*>(temp1);


    b->another_func();

    delete temp1;
    return 0;
}

上述程序的输出 -

Hello from B::another_func

我很难理解以下问题 -
1) For statement 1我们如何将父对象转换为子对象。逻辑上这应该是不正确的,因为现在我们已经扩展了这个对象的能力(同一个对象现在可以访问子类函数another_func)。

Java 中的类似代码会产生错误 -

"不兼容的类型:A不能转换为B
B b = (A)温度;"

现在,如果我们注释语句 1 并取消注释语句 2,语句 2 会发生类似的情况。

那么,为什么 C++ 允许这样做呢?

2) 现在,如果我们从两个类中删除 sayhello() 函数,使得它们不具有多态关系,则作为 dynamic_cast 的语句 2 不再起作用(正如我们所知,dynamic_cast 可以向下转换多态类),而是语句 1 的 c 样式转换仍然产生相同的输出。
所以我们可以说c风格的转换不是基于类之间的多态关系。
c-style cast 发生的参数是什么?

【问题讨论】:

标签: c++ casting


【解决方案1】:

C 风格的演员表未选中。在您的情况下,它只是告诉编译器将指针temp1 视为B 类型。对于您的代码,这是一个谎言,并且以任何形式使用此指针都会导致未定义的行为。未定义行为的一个结果是它会按照您的期望进行。 C 风格的强制转换是从 C 中“继承”的,并且需要获得大量 C 代码以使用 C++ 进行编译。

有时 C 风格的强制转换会做正确的事,但它们可以执行多种不同的转换,并且通常不能立即清楚它们执行哪种操作:强制转换 (T)x 可以等效于 reinterpret_cast&lt;T&gt;(x)、@987654325 @、const_cast&lt;T&gt;(x) 或其组合。通常,只有static_cast&lt;T&gt;(x)const_cast&lt;T&gt;(x) 的组合会产生明确定义的结果,即使这样,也存在未检查的先决条件。例如,使用static_cast&lt;D*&gt;(p) 将指针p 转换为基B 到派生D 的指针仅在p 是从指针D 通过直接转换D 生成时才定义。指向B 指针的指针。

【讨论】:

    【解决方案2】:

    假设您切换 cmets 以便注释 C 样式转换,而取消注释 dynamic_cast。此外,修改代码如下:

    B* b = dynamic_cast<B*>(temp1);
    if(b == nullptr)
        cout << "not really a B" << endl;
    else
        b->another_func();
    

    然后在运行代码时,它会打印出“not really a B”。

    如果你看what dynamic_cast does

    如果转换成功,dynamic_cast 返回一个 new_type 类型的值。如果转换失败并且 new_type 是指针类型,则返回该类型的空指针。

    所以您使用 dynamic_cast 的版本是未定义的行为,因为您取消引用指针而不检查转换是否失败。


    现在介绍使用 C-Style 演员表的所有不同版本。 The rules 是:

    当遇到 C 风格的转换表达式时,编译器会尝试将其解释为以下转换表达式,顺序如下:

    a) const_cast(表达式);

    b) static_cast(expression),带扩展:即使基类不可访问(即,此转换忽略私有继承说明符)。同样适用于将指向成员的指针转换为指向明确非虚基成员的指针;

    c) static_cast(带扩展)后跟 const_cast;

    d) reinterpret_cast(表达式);

    e) reinterpret_cast 后跟 const_cast。 选择满足相应强制转换运算符要求的第一个选项,即使它无法编译(参见示例)。如果转换可以以多种方式解释为 static_cast 后跟 const_cast,则无法编译。

    根据这些规则,您的 C 风格转换相当于

    B *b = static_cast<B *>(temp1);
    

    由于temp1 实际上指向A 对象,取消引用结果也是未定义的行为(但出于不同的原因)。


    一些一般要点:

    1. C++ 类型转换是 C 风格类型转换的细粒度版本。如果你必须投,更喜欢他们。您可以向编译器传达您要转换的方面。

    2. 在任何情况下,您都应该尽量避免强制转换。如果您确实使用dynamic_cast,请检查结果。

    【讨论】:

    • static_cast 不会给出未定义的行为。尝试取消引用 b 确实如此。 static_cast 提供未定义行为的唯一形式(仅在 C++17 中提出)是将超出范围的数值类型转换为枚举类型。
    • @Peter OMG,这对我来说真的很糟糕。非常感谢!您的评论很有帮助。
    • @Peter:天哪!您能否提供该提案的链接?编辑:Found it.
    猜你喜欢
    • 2013-05-16
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多