2012-05-04 5 views
4

Эта программа работает без каких-либо исключений. Не могли бы вы объяснить, почему успешные динамические статические и статические роли? И как 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; 
    } 

ответ

4

Давайте начнем с того, почему он не бросает каких-либо исключений, так как это довольно просто: dynamic_cast бросает исключение при попытке бросить ссылочный типа, и он выходит из строя. Когда вы используете 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. Это будет успешным/неудачным на основе того, является ли фактический тип объекта pointee (т. Е. Динамического типа) целевой мишенью (или базой) или нет. В этом случае отказ сигнализируется по-разному в зависимости от того, работаете ли вы с указателями или ссылками.Для справки отказ приведет к исключению. Для указателя ошибка приведет к созданию нулевого указателя.

+0

Можете ли вы объяснить, почему статические роли также успешны? – unj2

+0

@ kunj2aan: 'static_cast' даже не пытается выяснить, действительно ли выполнено преобразование. В вашем случае это, вероятно, работает в основном потому, что ваши функции-члены не используют никаких данных класса, поэтому обращение к ним с помощью указателя 'this', указывающего на неправильные данные, не вызывает видимых вреда/проблем. –

+0

Привет. Я изменил код с новыми функциями-членами, и программа все еще работает без проблем. Это ты имел в виду? – unj2

3

Зачем ему это? dynamic_cast возвращает NULL, если приведение не выполнено, оно не создает исключения для указателей.

Кроме того, что в стороне, почему это должно потерпеть неудачу? Оба Square и Circle - Shape s. Так что отливка прекрасна.

Результат такой, какой вы видите из-за базового полиморфизма. У вас есть Shape*, который указывает на объект Square или на объект Circle. Функция virutal вызывает вызов метода overriden из самого производного класса. Это центральный аспект ООП.

В большинстве случаев это достигается через virtual function tables. Хотя у вас есть Shape*, объект содержит указатель на таблицу виртуальных функций внутри. Поскольку этот член не изменяется во время трансляции, он указывает таблицу виртуальных функций Square или Circle, поэтому вызовы разрешаются правильно.

enter image description here

+0

Как примечание, OP, вставляющий 'Circle *' и 'Square *' в' vector ', а затем, когда он итерации, отбрасывает как квадрат *, так и 'круг * , Таким образом, OP накладывает круг на квадрат (через общую базу Shape). –

1

Вы используете dynamic_cast конвертировать из производного типа в базовый тип и нет никакой двусмысленности. Почему вы ожидали, что это потерпит неудачу? Кроме того, использование dynamic_cast на указателях не будет генерировать исключение (вместо этого оно вернет NULL). Он может генерировать исключение при литье между ссылками типа.

Ваш static_cast является успешным благодаря тому, как ваши простые классы построены. Так как в и SquareFunction() на самом деле не пытаются получить доступ к каким-либо недопустимым переменным-членам класса, то программа может успешно работать (к счастью), неправильно преобразовывая между типами, используя static_cast, это не то, что вы должны делать.

0

Короткий ответ: Когда вы делаете static_cast<Square> на указателе круга и static_cast<Circle> на указателе площади, вы зашли в неопределенное поведение. В этот момент может произойти что угодно.

Кроме того, при вызове dynamic_cast он может выйти из строя и вернуть NULL, если вы используете указатель. Он генерирует только исключения, если вы переходите к ссылке. Кроме того, компилятор может выбрать замену dynamic_cast компиляцией времени, если во время компиляции он знает, что нет никакой двусмысленности (что имеет место здесь, так как вы кастинг от child-> parent).

Выход, который вы получаете, состоит в том, что классы достаточно похожи друг на друга, что, в то время как роли не являются законными, им все равно удалось работать. Учитывая неопределенное поведение, нельзя полагаться на это, если вы продолжаете вносить изменения.

Смежные вопросы