【问题标题】:upcasting and downcasting c++向上转型和向下转型 C++
【发布时间】:2015-07-30 19:16:06
【问题描述】:

我正在玩弄指向基类的指针,我将一个矩形指针转换为一个圆形指针,并从矩形中调用printradius() 函数!有人可以解释为什么允许这样做吗?谢谢。

#include <iostream>
#include <string>
using namespace std;

class Shape {
};

class Circle: public Shape {
    private:
        double radius;
    public:
        Circle(double r)
        { radius = r;}
    void printradius()
        { cout << "circle's radius is " << radius << endl;}
};

class Rectangle: public Shape {
    private:
        double width, length;
    public:
        Rectangle(double l, double w)
        { length = l; width = w;}
};

int main() {

    Rectangle r( 2.0, 2.0);   // only a rectangle is created
    Shape* s = &r;            // up cast into a shape
    Circle* c = static_cast<Circle*>(s); //down cast into a circle
    c->printradius();

}

输出:

圆的半径是2

【问题讨论】:

  • 这是简单的未定义行为。
  • 顺便说一句,您应该在构造函数Rectangle(double l, double w):length(l),width(w){} 中使用初始化语法。至于您的问题,可能是因为从Circle 类开始的内存偏移量恰好与Rectangle 类中宽度或长度之一的内存偏移量相同。因此,当调用print_radius 时,会查找某个偏移量,巧合 会查找一个有效的双精度。你不应该依赖它,因为它是未定义的行为。假设内存布局发生变化或其他实现会破坏其他实现。

标签: c++ function virtual upcasting


【解决方案1】:

“允许”是什么意思?

该语言明确指出此类static_cast 的结果是未定义的,因此在这种情况下它并不是真正“允许”的。为了执行从Shape *Circle * 的有效向下转换,您必须确保Shape * 实际上指向Circle 或派生自Circle 的东西。否则,行为未定义。

至于为什么编译器没有捕捉到它......编译器不可能捕捉到依赖于运行时条件的错误。在一般情况下,编译器不知道您的 s 指针实际指向什么。

至于为什么该语言甚至提供这样的功能......它提供它是因为如果正确使用它会非常有用。

【讨论】:

  • static_cast 的结果是未定义的”——不正确!在处理标准布局类(这些是)时,您可以检查非静态数据成员的公共初始子序列。当在不匹配的对象指针上调用成员函数时,会出现未定义的行为,因为成员函数调用不属于特殊权限。
  • @Ben Voigt:在 C++14 的最终版本中是否有所改变?由于我正在查看的草稿 (n3690) 在这方面一直具有限制性:5.2.9 静态转换“11 类型为“指向 cv1 B 的指针”的纯右值,其中 B是类类型,可以转换为“指向 cv2 D 的指针”类型的纯右值,其中 D 是从 B 派生的类 [...] 如果“指向 cv1 B 的指针”类型的纯右值指向一个 B,即实际上是 D 类型对象的子对象,结果指针指向 D 类型的封闭对象。否则,行为未定义。"
  • 嗯,我能找到的常见初始序列的实际规则只提到联合......但我看到这一直是使用指针完成的。可能为了使其正式合法,应该通过第一个成员的类型进行转换。哦,我明白了,这种用法需要reinterpret_cast(明确提到标准布局类),而不是static_cast
【解决方案2】:

静态转换是“因为我这么说。”,换句话说,相信我,程序员,事情就是我所说的,在这种情况下是一个圆指针。当调用printradius() 方法时,this 指针指向r,而当printradius() 碰巧去引用寻找半径时,它找到了第一个double,值为2.0。以这种方式运行没有崩溃或报告错误,但如您所知,这没有任何意义。

使用动态转换并检查返回的值。你会看到 null,因为矩形和圆形不一样。

【讨论】:

  • 这些类型不是多态,所以你不能使用dynamic_cast
  • 没错。我确实指出他应该期待空值。 “多态”是说“矩形和圆形不一样”的好词。 :-D
  • 它不会产生null,因为它不会编译
  • @BenVoigt,的确,你是对的。我学到了一些东西。我的经验一直是多态类,并且编译效果很好。但是在这种情况下,即使两个类都继承自同一个基类,编译器(和你)发现它们太不同了,它不会编译。我的立场是正确的。
  • 这不是“太不同”的问题。这里没有virtual 成员函数,dynamic_cast 使用虚函数元数据(通常是作为指针存储在对象中的虚成员函数表的地址)来确定对象的动态类型和演员表是否可能。没有虚函数——不允许dynamic_cast。 (迂腐——标准不需要比较 vtbl 指针,但是虚函数的存在放宽了布局规则并允许编译器添加任何需要的额外信息)
猜你喜欢
  • 2016-05-08
  • 2013-02-17
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多