Ошибки, баги, вопросы - страница 1890

 
Renat Fatkhullin:

Все верно, нельзя приводиться снизу вверх, только сверху вниз.

В смысле нельзя приводить от базового класса к потомку?
 
Комбинатор:
В смысле нельзя приводить от базового класса к потомку?

Да, в случае когда на базовом не лежит реально сконструированный потомок.

 
Renat Fatkhullin:

Все верно, нельзя приводиться снизу вверх, только сверху вниз. Это ради безопасности.

если мы приведем указатель сверху вниз, т.е. к родителю, после чего передадим указатель куда нить в другую область видимости, то там будут доступны поля потомка?
 
Renat Fatkhullin:

Да.

Ренат, вы в курсе что такое dynamic_cast?
 
Комбинатор:
Ренат, вы в курсе что такое dynamic_cast?

Конечно.

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

 

В документации прямо этот же пример рассмотрен и объяснен. Динамический кастинг при исполнении срабатывает только после прохождения проверки системы безопасности и допустимости преобразований. У каждого объекта MQL5 есть вся мета-информация для проверки прав во время исполнения. Это не пустой С++.


Динамическое приведение типов  с помощью оператора dynamic_cast

Существует возможность динамического приведения типов с помощью оператора dynamic_cast, который может быть применён только к указателям классов. При этом проверка корректности типов производится в момент выполнения программы. Это означает, что при использовании оператора dynamic_cast компилятор не производит проверку типа данных, используемого для приведения. В случае, если осуществляется преобразование указателя к типу данных, который не является фактическим типом объекта, результатом  будет  значение NULL.

dynamic_cast <type-id> ( expression )

Параметр type-id в угловых скобках должен быть указателем на ранее определённый тип класса. Тип операнда expression (в отличии от C++) может быть любым, кроме void.

Пример:

class CBar { };
class CFoo : public CBar { };
 
void OnStart()
  {
   CBar bar;    
//--- динамическое приведение типа указателя *bar к указателю *foo разрешено 
   CFoo *foo = dynamic_cast<CFoo *>(&bar); // критической ошибки выполнения не возникнет   
   Print(foo);                             // foo=NULL      
//--- попытка явного приведения ссылки объекта типа Bar к объекту типа Foo запрещено
   foo=(CFoo *)&bar;                       // возникнет критическая ошибка выполнения
   Print(foo);                             // эта строка не будет выполнена
  }

 
Renat Fatkhullin:

Конечно.

Точно? Потому что dynamic_cast чаще всего как раз нужен для приведения снизу вверх, от родителя к потомку.

Мало того, в MQL он отлично приводит снизу вверх даже в том случае когда не должен:

class A
{
public:
   virtual void f()
   {
      Print("1");
   }
};

class B: public A
{
public:
   virtual void f()
   {
      Print("2");
   }
};

void OnStart()
{
   A* ptr1 = new B();
   ptr1.f();
   A* ptr2 = new A();
   ptr2.f();
   
   B* casted = dynamic_cast<B*>(ptr1);
   casted.f();
   
   B* casted1 = dynamic_cast<B*>(ptr2);
   casted1.f(); // здесь должна быть ошибка потому что casted1 должен быть null
   delete ptr1;
   delete ptr2;
}

вывод:
2017.05.13 18:30:14.864    t ETHUSD,M5: 2
2017.05.13 18:30:14.865    t ETHUSD,M5: 1
2017.05.13 18:30:14.866    t ETHUSD,M5: 2
2017.05.13 18:30:14.867    t ETHUSD,M5: 2


Renat Fatkhullin:

Вы посмотрите на обсуждаемый кусок кода MQL5.

Да, он не должен работать, и выше уже объяснили почему, но совсем не потому что каст снизу вверх невозможен.

 
Konstantin:
если мы приведем указатель сверху вниз, т.е. к родителю, после чего передадим указатель куда нить в другую область видимости, то там будут доступны поля потомка?

Да, вот пример, демонстрирующий ваш вопрос:

class CLASS1
  {
public:
   int               i;
  };
class CLASS2 : public CLASS1
  {
  };
void OnStart()
  {
   CLASS1 _object;
   CLASS2 *_ptr=dynamic_cast<CLASS2 *>(&_object);

   if(!_ptr)
      Print("CLASS1 -> CLASS2 failed, null");

   CLASS2 *my=new CLASS2;
   CLASS1 *my_ptr=my;
   CLASS2 *my_ptr2=dynamic_cast<CLASS2 *>(my_ptr);

   if(my_ptr2)
     {
      Print("CLASS2 -> CLASS1 -> CLASS2 ok");
      my_ptr2.i=1;
     }
   Print("Value: ",my.i);
  }
и вывод:
2017.05.13 18:34:50.341 cast (EURUSD,H1)        CLASS1 -> CLASS2 failed, null
2017.05.13 18:35:18.933 cast (EURUSD,H1)        CLASS2 -> CLASS1 -> CLASS2 ok
2017.05.13 18:35:20.110 cast (EURUSD,H1)        Value: 1

Сначала проверяем неразрешенный кастинг снизу вверх и получаем NULL. Все верно.

Потом создаем объект CLASS2, присваиваем ссылку на него родительскому классу (тут важно понять, что в динамике среда знает, что исконный тип объекта CLASS2 - это хранится в его метаинформации). Далее (как раз ваш вопрос) динамически кастим (с проверкой права преобразования на основе метаинформации исходного объекта) из ссылки CLASS1 в CLASS2.

Проверяем результат кастинга и прописываем в переменную i = 1. В финале выводим значение i, обращаясь к исходно созданному объекту.

Все работает верно и по спецификации (включая dynamic_cast спеку самого C++).

 
Комбинатор:

Точно? Потому что dynamic_cast чаще всего как раз нужен для приведения снизу вверх, от родителя к потомку.

Мало того, в MQL он отлично приводит снизу вверх даже в том случае когда не должен:

Точно:

Не забывайте обновляться на последние билды. Я сейчас тестируюсь на 1598, который выкладывался недавно в виде зипов в этой ветке вроде.

 
Renat Fatkhullin:

Не забывайте обновляться на последние билды.

Да, старый билд.

Renat Fatkhullin:

Все верно, нельзя приводиться снизу вверх, только сверху вниз. Это ради безопасности.

Вы вот это потрите, вводит в заблуждение и напрямую противоречит функционированию dynamic_cast