【问题标题】:Strange behavior of dynamic cast and static cast动态投射和静态投射的奇怪行为
【发布时间】:2012-05-14 03:19:03
【问题描述】:

这个程序运行没有任何异常。你能解释一下为什么动态转换和静态转换都成功了吗?以及 C++ 如何设法解析所需的虚函数?

class Shape 
    {
    private :
        string helperFunction();

    public : 
        virtual void draw() = 0;
        virtual void type();
    };

void Shape::type()
    {
    cout << "Shape Type";
    }

// ----------------------------------------------------------------------------

class Circle : public Shape
    {
    private: 
    string circlething;

    public :
        virtual void draw();
        virtual void type();
        void CircleFunction();
    };

void Circle::draw()
    {
    cout <<"Circle Draw" << endl;
    }

void Circle::type()
    {
    cout <<"Circle Type" << endl;
    }

void Circle::CircleFunction()
    {
    circlething = "Circle Thing";
    cout << circlething;
    cout << "Circle Function" << endl;
    }


class Square : public Shape
    {
    private :
        string  squarething;
    public :
        virtual void draw();
        virtual void type();
        void SquareFunction();
    };

void Square::draw()
    {
    cout <<"Square Draw" << endl;
    }

void Square::type()
    {
    cout <<"Square Type" << endl;
    }

void Square::SquareFunction()
    {
    squarething = "Square Thing";
    cout << squarething;
    cout << "Square Function" << endl;
    }
// ----------------------------------------------------------------------------


int _tmain(int argc, _TCHAR* argv[])
    {
    vector<Shape *> shapes;
    Circle circle;
    Square square;
    shapes.push_back(&circle);
    shapes.push_back(&square);

    vector<Shape *>::const_iterator i;

    for (i = shapes.begin(); i < shapes.end(); ++i)
        {

        cout << "\n*** Simple Type ***\n" << endl;
        (*i)->type();
        (*i)->draw();

        cout << "---Static Cast Circle--" << endl;
        Circle* circle = static_cast<Circle*>(*i);
        circle->type();
        circle->draw();
        circle->CircleFunction();

        cout << "---Static Cast Square--" << endl;
        Square* square = static_cast<Square*>(*i);
        square->type();
        square->draw();
        square->SquareFunction();

        cout << "---Static Cast Circle to Shape --" << endl;
        Shape* shape1 = static_cast<Shape*> (circle);
        shape1->type();
        shape1->draw();

        cout << "--- Dynamic Cast Circle to Shape --" << endl;
        Shape* shape2 = dynamic_cast<Shape*> (circle);
        shape2->type();
        shape2->draw();

        cout << "--- Static Cast Square to Shape --" << endl;
        Shape* shape3 = static_cast<Shape*> (square);
        shape3->type();
        shape3->draw();

        cout << "--- Dynamic Cast Square to Shape --" << endl;
        Shape* shape4 = dynamic_cast<Shape*> (square);
        shape4->type();
        shape4->draw();

        }
    int x;
    cin >> x;
    return 0;
    }

【问题讨论】:

    标签: c++ oop casting virtual-functions dynamic-cast


    【解决方案1】:

    为什么要这样做? dynamic_cast 返回 NULL 如果转换失败,它不会抛出指针异常。

    此外,除此之外,它为什么会失败? SquareCircle 都是 Shapes。所以演员阵容很好。

    由于基本的多态性,输出如您所见。您有一个 Shape* 指向 Square 对象或 Circle 对象。 virutal 函数调用从最派生类调用重写方法。这是 OOP 的一个中心方面。

    在大多数实现中,这是通过virtual function tables 实现的。尽管您有一个Shape*,但该对象包含一个指向内部虚函数表的指针。因为该成员在强制转换期间没有改变,它指向SquareCircle 的虚函数表,所以调用被正确解析。

    【讨论】:

    • 就像注释一样,OP 将Circle*Square* 插入vector&lt;Shape*&gt;,然后在迭代时将@ 转换为Square*Circle* .所以 OP 正在将 Circle 转换为 Square(通过共享的 Shape 基础)
    【解决方案2】:

    让我们从为什么它不抛出任何异常开始,因为它非常简单:dynamic_cast 在您尝试转换 reference 类型时抛出异常,但它失败了。当你在指针上使用dynamic_cast时,如果成功则返回一个指针,如果失败则返回一个空指针。

    至于为什么你从来没有失败的演员表,你所有的dynamic_cast 都是向上层次结构,产生一个Shape *。由于所有有问题的对象都是Shape派生的,所以这总是会成功——事实上,这种转换可以隐式完成。

    为了演示dynamic_cast 的真正意图,让我们编写一些稍微不同的代码:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Shape {
    public : 
        virtual void draw() = 0;
    };
    
    class Circle : public Shape
    {
    public :
        virtual void draw() { cout << "Circle Draw\n"; }
        void circlefunc() { cout << "circle func\n"; }
    };
    
    class Square : public Shape
    {
    public :
        virtual void draw() { cout << "Square Draw\n"; }
        void squarefunc() { cout << "Square Func\n"; }
    };
    
    int main() {
        vector<Shape *> shapes;
        Circle circle;
        Square square;
        shapes.push_back(&circle); // implicit conversion from Circle * to Shape *
        shapes.push_back(&square); // implicit conversion from Square * to Shape *
    
        Circle *c;
        Square *s;
        for (int i=0; i<shapes.size(); i++) {
            shapes[i]->draw(); // draw polymorphically
            if (c = dynamic_cast<Circle *>(shapes[i])) // try to cast to Circle *
                c->circlefunc();               // if it worked, invoke circlefunc
            else if (s = dynamic_cast<Square *>(shapes[i])) // likewise for square
                s->squarefunc();
        }
        return 0;
    }
    

    这次我们使用 dynamic_cast 从Shape *Square *Circle *。当且仅当指针对象的动态类型是该类型时,这才会成功。否则它将产生一个空指针。当且仅当我们获得一个非空指针时,我们才会调用该特定类型的成员函数。

    总结一下:当你有一个派生对象的指针/引用时,你可以隐式转换为基类的指针/引用。您不需要dynamic_cast(或任何显式转换),因为这始终是安全的。

    当您从指针/引用转换为基以获取指向派生的指针/引用时,然后您通常希望使用dynamic_cast。这将根据指针对象的实际类型(即动态类型)是否是所需的目标(或其基础)而成功/失败。在这种情况下,失败的信号会有所不同,具体取决于您使用的是指针还是引用。作为参考,失败会抛出异常。对于指针,失败会产生空指针。

    【讨论】:

    • 你能解释一下为什么静态转换也成功了吗?
    • @kunj2aan: static_cast 甚至不会尝试确定您尝试的转换是否有效。在您的情况下,它可能似乎主要是因为您的成员函数不使用任何类数据,因此使用指向错误数据的 this 指针调用它们不会造成明显的危害/问题。
    • 嗨。我用新的成员函数修改了代码,程序仍然没有任何问题。你是这个意思吗?
    【解决方案3】:

    您正在使用dynamic_cast 从派生类型转换为基类型,并且没有歧义。你为什么认为它会失败?此外,在指针上使用dynamic_cast 不会引发异常(而是返回NULL)。在类型引用之间进行强制转换时会抛出异常。

    您的static_cast 之所以成功,是因为您的简单类的构造方式。由于CircleFunction()SquareFunction() 实际上并没有尝试访问任何无效的类成员变量,因此程序能够成功运行(幸运的是),使用static_cast 在类型之间错误地转换不是你应该做的事情。

    【讨论】:

      【解决方案4】:

      简短回答:当您在 Circle 指针上执行 static_cast&lt;Square&gt; 并在 Square 指针上执行 static_cast&lt;Circle&gt; 时,您已经进入了未定义的行为。那时几乎任何事情都可能发生。

      此外,当您调用 dynamic_cast 时,如果您正在转换为指针,它可能会失败并返回 NULL。如果您要转换为引用,它只会引发异常。此外,如果编译器在编译时知道没有歧义,则编译器可以选择用编译时强制转换替换 dynamic_cast(这里就是这种情况,因为您是从子级->父级强制转换的)。

      您得到的输出是因为这些类彼此之间足够相似,因此虽然强制转换不合法,但它们仍然可以工作。鉴于未定义的行为,如果您继续进行更改,就不可能依赖它。

      【讨论】:

        猜你喜欢
        • 2020-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-16
        • 2019-09-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-11
        相关资源
        最近更新 更多