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

 
A100:

Унификация нужна, чтобы один и тот же код работал одинаково с С++ и MQL. В С++ такой порядок неспроста (!) сделан (в ином случае на каком то этапе возникают неоднозначности). Первоначально я подумал, что у С++ компилятора нелогичность и/или даже ошибка, но сейчас склоняюсь к тому, что есть объективные причины такого подхода.  Вот пример на MQL, который показывает, что полное совпадение у X:: g, а вызывается все равно Y::g - как в С++, что противоречит предыдущему примеру https://www.mql5.com/ru/forum/1111/page1223#comment_1074757

Результат: 3

Получается, что у C++ "нелогичный", но однозначный порядок, а у MQL - случайный порядок - при точном совпадении типов в одном случае вызывается базовый класс, а в другом - производный

Похоже, что Вы ошиблись в данном примере, если класс Y наследовать от X, а не от C, то поведение будет соответствовать предыдущему примеру.
 
stringo:

Что сказано в стандарте С++ про порядок выбора перегруженных функций?

Цитирую Строуструпа: "C++ требует точного соответствия типов виртуальной функции в базовом классе и подменяющей ее функции в производном классе"

Сейчас я разобрался в вопросе и пришел к выводу о правильности С++. В обоих приведенных выше примерах g() производного класса скрывает (а не подменяет) g() базового класса. А механизм виртуальности включается только когда идет подмена, а для подмены нужно точное соответствие типов (кроме возвращаемого). От конкретной реализации может зависеть только точность возвращаемого типа.

 
mql5:
Похоже, что Вы ошиблись в данном примере, если класс Y наследовать от X, а не от C, то поведение будет соответствовать предыдущему примеру.

Да здесь ошибся, таким образом получается и у С++ и у MQL однозначные, но разные порядки подмены и сокрытия. Посмотрите пожалуйста мои доводы в предыдущем посте о различии между подменой и сокрытием

Тот факт, что С++ при сокрытии выдает предупреждение, говорит о том, что такой порядок выбран умышленно

class A {};
class B : public A {};

class X {
public:
        virtual int g( A* a )
        virtual int f( B* a )
};
class Y : public X {
public:
        virtual int g( A* a ) //подмена  X::g
        virtual int f( A* a ) //сокрытие X::f
};
 
A100:

Да здесь ошибся, таким образом получается и у С++ и у MQL однозначные, но разные порядки подмены и сокрытия. Посмотрите пожалуйста мои доводы в предыдущем посте о различии между подменой и сокрытием

Тот факт, что С++ при сокрытии выдает предупреждение, говорит о том, что такой порядок выбран умышленно

Компилятор MQL при поиске метода(функции) не считает переход к родителю за кастинг, как похоже это делает например C++ от MS.

Спасибо за сообщение, мы обсудим(подумаем) это.

 
mql5:
Компилятор MQL при поиске метода(функции) не считает переход к родителю за кастинг, как похоже это делает например C++ от MS.

Спасибо за сообщение, мы обсудим(подумаем) это.

Обратите внимание, что не только от MS - я использовал Borland C++ 5.5.1

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

 
A100:

Цитирую Строуструпа: "C++ требует точного соответствия типов виртуальной функции в базовом классе и подменяющей ее функции в производном классе"

Сейчас я разобрался в вопросе и пришел к выводу о правильности С++. В обоих приведенных выше примерах g() производного класса скрывает (а не подменяет) g() базового класса. А механизм виртуальности включается только когда идет подмена, а для подмены нужно точное соответствие типов (кроме возвращаемого). От конкретной реализации может зависеть только точность возвращаемого типа.

Подождите, подождите. Речь идёт о перегрузке функций с разными параметрами.

Виртуальные же функции всегда имеют параметры того же самого типа. Виртуальные функции - вне этой нашей дискуссии

 
A100:

Обратите внимание, что не только от MS - я использовал Borland C++ 5.5.1

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

Обсудили, менять поведение компилятора MQL не будем - "Палка о двух концах". Поясню.

Проблема актуальна, только при внесении изменений в готовые классы(рабочую программу).
После появления у родителя более подходящей функции программа вдруг перестаёт работать, т.к. вместо метода потомка стал вызываться метод родителя.
Хорошо, если изменить поведение MQL, то может возникнуть похожая ситуация - при добавлении нового метода в потомка, вместо функции родителя (без кастинга параметров) начал вызываться метод потомка, при чём с кастингом параметра и программа опять перестаёт работать.

Таким образом, никаких проблем кроме совместимости с C++ решено не будет, кроме того, изменение поведения компилятора коснётся существующих MQL программ.
 

Так как переменные типа структур и простых типов не имеют указателей, то применять к ним функцию GetPointer() запрещено.

Также запрещается в качества аргумента функции (какой ???  GetPointer или вообще любой ? ) передавать указатель. Во всех перечисленных случаях компилятор сообщит об ошибке.

 Цитата с документации.

class A{
public:
   void operator= (const A* from){
      if (GetPointer(this) == GetPointer(from))      // а тут я передаю и все ок
         Alert(1);                                  // резутат 1  из за того что передаю this
      else
         Alert(2);
   }
};

class B : public A{
public:
   void operator= (const B& from){  
      if (GetPointer(this) == GetPointer(from))
         return;
      operator=((A*)(GetPointer(this)));
   }
};
  B b;
   B bb;                        // забыл написать создание объектов и вызов функции.
   b = bb;

 По чему не ругается ??? за алерты уже понял - сам виноват не то в функцию передавал. 

 
ALXIMIKS:

 Также запрещается в качества аргумента функции (какой ??? GetPointer или вообще любой ? ) передавать указатель.

Наверное имелся ввиду указатель на структуру и/или простой тип (явно там не указано, но из контекста следует). Указатель на класс как аргумент функции там даже ниже в примере есть

class CItem
  {
   CItem*            m_next;
   void              Next(CItem *item) { m_next=item; }
  };

А если имелась ввиду именно GetPoiner(), то в Вашем примере нет иной возможности преобразования указателя from к объекту (кроме использованной) и поэтому такой запрет не имеет смысла

//даже такую запись сложно запретить
GetPointer(GetPointer(GetPointer(GetPointer(from))))
 

Почему в сериализации СList при загрузке данных с файла CList::Load(int file_handle) не продумана возможность работы с несколькими калассами?

virtual CObject* CreateElement();  

это функция для создания только для одного класса объектов. А если у меня их 5 и тип следующего записан в файле под условным номером. 

Все просто решалось если была бы еще одна загрушка:

 virtual CObject* CreateElement(int file_handle);  

Да, не проблема перекопировать целую функцию чтобы дописать в нее эту строку, но почему бы сразу не продумать.

Или я что-то не догнал как правильно использовать.