Вопросы по ООП в MQL5 - страница 45

 
Dmitry Fedoseev:

А кто-нибудь объяснит, чем такая инициализация полей:

лучше чем такая:

И вообще, в чем смысл?

const-поля можно инициализировать.

 
Dmitry Fedoseev:

А кто-нибудь объяснит, чем такая инициализация полей:

лучше чем такая:

И вообще, в чем смысл?

В первом случае у тебя инициализация, а во втором случае - уже для дефолтно проинициализированных полей выполняются операции. Так что тут все от компилятора зависит, либо без разницы, либо хренова туча лишних действий в run-time. В плюсах плохим тоном второй вариант считается.
 
Оператор =, как и положено, возвращает r-value (5<(val=7) работает), а вот как раз с семантикой переноса, тут нет её, а просили, грустно, поэтому и конструктора переноса нет.
 
Igor Makanu:

вопрос чисто теоретический:

видел, возможно в СБ, такой вызов конструктора:

чем будет отличаться такой код:

распринтовал, разницы не увидел, тогда немного конкретнее - что дает или зачем можно использовать принудительный вызов конструкторов обьектов a1 и a2?

в чем "удобство" первого варианта? 

Первый вариант называется - Value initialization, второй - Default initialization. Имеются там свои тонкости и различий в зависимости от типа объекта (агрегат ли, с дефолтным конструктором ли, ...). Например, здесь будет абсолютно разный результат при использовании скобок и без:

#include <iostream>
using namespace std;
struct S {
        int i;
        int q;
        int f;
};
struct Q : S {
        Q():S() {}
        //Q() {}
};
int main() {
        Q s;
        cout << s.i << s.q << s.f << endl;
}

Если хочется в теорию по самые уши, то сюда https://en.cppreference.com/w/cpp/language/initialization

ЗЫ: для мкл не всё это актуально, но в качестве понимания эталонной модели.

Initialization - cppreference.com
  • en.cppreference.com
Initialization of a variable provides its initial value at the time of construction. The initial value may be provided in the initializer section of a declarator or a new expression. It also takes place during function calls: function parameters and the function return values are also initialized. For each declarator, the initializer may be one...
 
Vict:

Если хочется в теорию по самые уши, то сюда https://en.cppreference.com/w/cpp/language/initialization

пока нет необходимости, но сохраню, буду знать где почитать первоисточник

Спасибо всем!

Vladimir Simakov:
В плюсах плохим тоном второй вариант считается.

будет та же проблема с которой я столкнулся - дублирование кода инициализации полей, когда возникнет необходимость написать в классе более одного конструктора

если использовать 2-й вариант, то повторяющийся код можно вынести в отдельный метод и вызвать этот метод после выполнения необходимых действий в каждом конструкторе (у меня инициализация класса структурой и 2-й вариант именем фала, где записаны данные этой структуры (резервное сохранение))


писать, что повторять код это плохо... не интересно, а вот проблема, что если в класс добавлю новое поле, то придется не забыть проинициализировать это поле столько раз, сколько конструкторов - то, что это не удобно, не суть, а вот то, что это можно забыть сделать N-раз, это проблема, имхо

 
Igor Makanu:

пока нет необходимости, но сохраню, буду знать где почитать первоисточник

Спасибо всем!

будет та же проблема с которой я столкнулся - дублирование кода инициализации полей, когда возникнет необходимость написать в классе более одного конструктора

если использовать 2-й вариант, то повторяющийся код можно вынести в отдельный метод и вызвать этот метод после выполнения необходимых действий в каждом конструкторе (у меня инициализация класса структурой и 2-й вариант именем фала, где записаны данные этой структуры (резервное сохранение))


писать, что повторять код это плохо... не интересно, а вот проблема, что если в класс добавлю новое поле, то придется не забыть проинициализировать это поле столько раз, сколько конструкторов - то, что это не удобно, не суть, а вот то, что это можно забыть сделать N-раз, это проблема, имхо

Макрос, который и параметрическим может быть. Забыть (сам страдаю), тоже этим лечится)))).

 
Igor Makanu:

будет та же проблема с которой я столкнулся - дублирование кода инициализации полей, когда возникнет необходимость написать в классе более одного конструктора

если использовать 2-й вариант, то повторяющийся код можно вынести в отдельный метод и вызвать этот метод после выполнения необходимых действий в каждом конструкторе (у меня инициализация класса структурой и 2-й вариант именем фала, где записаны данные этой структуры (резервное сохранение))


писать, что повторять код это плохо... не интересно, а вот проблема, что если в класс добавлю новое поле, то придется не забыть проинициализировать это поле столько раз, сколько конструкторов - то, что это не удобно, не суть, а вот то, что это можно забыть сделать N-раз, это проблема, имхо

Да бросьте, всё решаемо:

class Q {
    void ctr_base(T t, S s) {//make init tasks
        }
public:
    Q(T t, S s, int i) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
    Q(T t, S s, strint str) {
        // make base init
        ctr_base(t, s);
        // make additional tasks
        ...
    }
};

Я не против дефолтных конструкторов (или default initialization), но если ваш конструктор оставляет объект (ну если это не тупой агрегат)  в неопределённом состоянии и потом вы его доинициализируйте через какие-то костыли, то делаете вы всё неправильно.

ЗЫ: кстати, в плюсах можно делегирующий конструктор, не хватает, конечно

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};
 
Vict:

Да бросьте, всё решаемо:

немного опередили меня, только за ПК сел

совет @Vladimir Simakov выполнить инициализацию полей класса с помощью макроса, чтоб не забыть правильно проинициализировать, ну как бы да - хороший прием, правда код будет читаться аналогично вызову метода который проинициализирует поля, сложно предположить насколько это хороший тон...


Ваш пример, тож не самый изысканный, да он решает проблему именно так как я сейчас сделал у себя - отдельный метод инициализации \

имхо, тут вопрос в целях - правильно писать базовый класс в котором будут все необходимые поля, наследоваться от него, сделать конструктор protected и инициализировать базовый класс от наследников тем самым будет защита от "забыть" - конструктора по умолчанию же нет? и будет защита от проинициализировать сторонним вызовом - конструктор же protected ? - так по крайней мере у Майкрософт можно посмотреть если гуглить "protected constructor" - будет статья


а у меня задача немного другая, специально ушел от наследования, чтобы сохранять состояние полей класса в файл, 2 поля у мня 2 класса, и их состояние тоже сохраняю в тот же файл путем вызова соответствующих методов. Пробовал все это сохранять когда наследовался от базового класса, очень запутанный схема получается, переписал без наследования,  сейчас все "прозрачно" стало

 
Igor Makanu:

немного опередили меня, только за ПК сел

совет @Vladimir Simakov выполнить инициализацию полей класса с помощью макроса, чтоб не забыть правильно проинициализировать, ну как бы да - хороший прием, правда код будет читаться аналогично вызову метода который проинициализирует поля, сложно предположить насколько это хороший тон...


Ваш пример, тож не самый изысканный, да он решает проблему именно так как я сейчас сделал у себя - отдельный метод инициализации \

имхо, тут вопрос в целях - правильно писать базовый класс в котором будут все необходимые поля, наследоваться от него, сделать конструктор protected и инициализировать базовый класс от наследников тем самым будет защита от "забыть" - конструктора по умолчанию же нет? и будет защита от проинициализировать сторонним вызовом - конструктор же protected ? - так по крайней мере у Майкрософт можно посмотреть если гуглить "protected constructor" - будет статья


а у меня задача немного другая, специально ушел от наследования, чтобы сохранять состояние полей класса в файл, 2 поля у мня 2 класса, и их состояние тоже сохраняю в тот же файл путем вызова соответствующих методов. Пробовал все это сохранять когда наследовался от базового класса, очень запутанный схема получается, переписал без наследования,  сейчас все "прозрачно" стало

А что тут запутанного?

...
virtual void CBase::Save(int hndl){...}
...
CObj:public CBase
...
void CObj::Save(int hndl){
   CBase::Save(hndl);
   ...
}
 
Vladimir Simakov:

А что тут запутанного?

именно от этого я и ушел, а сначала так и делал

при таком подходе - наследоваться от базового класса, "где все есть" - все работает, но до тех пор пока не захотим попробовать сделать несколько полей классов, а затем захотим еще в каждом поле-класс добавить несколько полей, ну и прибьем все это динамическим массивом классов

и получим, что сам метод Save(int hndl)- мы не можем реализовать в базовом классе, этот метод будет по сути интерфейсом, который, как обсуждали, да и не нужен вовсе - без интерфейсов тож можно же писать

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


а проблема будет, что при реализации сохранения в файл нужно будет разработать заголовок файла, в котором нужно будет описать общее количество разнотипных записей, потом нужно будет не потерять логику/порядок сохранения данных, чтобы правильно все прочитать.... и финал будет, если изменили хоть одно поле в одном классе

имхо, трудоемкость такого "легкого" наследования от базового класса - сводится к разработке и сопровождению небольшой базы данных, постоянный контроль за изменениями в любом классе