Qubc
@Qubc
Ненавижу полисемию.

Почему разрешен upcast в RTTI?

При итерировании указателей на базовый тип контейнере и необходимости преобразования некого типа можно проверять указатель производного типа на ноль и вызывать любые методы (виртуальные и нет) (или обрабатывать исключение при работе с ссылкой).
Но при этом, из-за безопасности upcast возможна ситуация, когда указатель базового типа A преобразуется в указатель промежуточного типа B, при том что на самом деле указатель хранит адрес объекта C.
В ручной реализации rtti (пример из книги внизу) можно запретить такие изменения. Но dynamic_cast их не запрещает. Значит, зачем-то это нужно. Вопрос - почему?

#include <iostream>

class A {
public:  
  int data;
  virtual ~A (void) {   }
  virtual void iam (void) { std::cout << "A \n"; }
};

class B : public A {
public:
  virtual ~B (void) {   }
  virtual void iam (void) { std::cout << "B \n"; }
  void special (void ) { std::cout << "B special \n"; }
};

class C : public B{
public:
  virtual ~C(void) {   }
  virtual void iam (void) { std::cout << "C \n"; }
  void special (void ) {std::cout << "C special \n";  }
};

int main(void) {
  A * aptr = nullptr;
  B * bptr = nullptr;
  C * cptr = new C;

  aptr = cptr;//good upcast
  bptr = dynamic_cast<C *> (aptr);// good downcast and unwanted upcast
  if (bptr){
    bptr -> iam(); // C 
    bptr -> special(); // B special 
  }
  delete cptr;

  return 0;
}


class Metal : public Investment {
    typedef Investment Super;
protected:
    enum { OFFSET = 4, TYPEID = BASEID + OFFSET };
public:
    virtual bool isA(int id) {
        return id == TYPEID || Super::isA(id);
        // return id == TYPEID ;
    }
    static Metal* dynacast(Security* s) {
        return (s->isA(TYPEID)) ? static_cast<Metal*>(s) : 0;
    }
};


for(vector<Security*>::iterator it = portfolio.begin();
 it != portfolio.end(); ++it) {
 Investment* cm = Investment::dynacast(*it);
if(cm)
 cm->special();
else
 cout << "not an Investment" << endl;
 }
  • Вопрос задан
  • 123 просмотра
Пригласить эксперта
Ответы на вопрос 1
@Mercury13
Программист на «си с крестами» и не только
1. Принцип подстановки Лисков — один из важных принципов организации интерфейса объекта. Смысл: само устройство абстракции должно допускать такое, что у нас сын C, но мы безопасно могли работать с отцовскими типами A и B.
Пример: Отец — прямоугольник с setWidth/setHeight. Сын — квадрат. Значит, уже при проектировании абстракции надо допустить, что setWidth/setHeight или изменит заодно и высоту, или откажется работать, или…
2. Принцип достаточности. Зачем работать с типом C, если достаточно с B?

UPD. А для чего этот dynamic_cast вообще? ООП без него проживёт, и часто dynamic_cast — это расписка в плохой конструкции объектов. Но в первую очередь он нужен на API настолько общие, что не совсем понятно, как сделать просто и эффективно. Или требование бинарной совместимости, что важно для всяких там VCL и Qt.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы