ООП, шаблоны и макросы в mql5, тонкости и приёмы использования - страница 2

 
Ilya Malev:

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

Да, верно, в обычных классах инициализируется сразу. А вот в шаблонных - нет:

template<typename T>
class A
{
 public: 
  static int a;
};

template<typename T>
int A::a=10;

int f() { return A<int>::a; }

int a= f();

void OnStart()
{
  Print(a); // Результат: 0
};
 
Alexey Viktorov:

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

В этом примере сначала будет инициализирована переменная static int b но пока ещё не инициализирована переменная static int f находящаяся внутри функции int a(int n) и в результате получим белиберду.

Кстати да, это же ещё один баг.  Т.е. помимо раздельной инициализации статических/глобальных перемеренных,  оно ещё и сами статические переменные инициализирует в неправильном порядке. И вот это уже даже посерьёзней проблема.  Вот вы говорите "не инициализировать другие статик-переменные теми методами или функциями в которых присутствуют статик-переменные".  Как вы это соблюдать в реальности предлагаете?  Каждая функция независима, существует сама по себе. У неё своя реализация, которая может меняться. Раньше допустим в ней не было статик-переменной, а потом вы решили добавить.  И значит где-то может что-то неправильно начать работать, ибо там закладывалось на то, что в функции не будет статической переменной.  И как вы это всё контролировать собираетесь.

 
Да и вообще мне непонятны такие решения, мол надо отказаться от того то, или того-то. Зачем отказываться?  Чего ради?  Если можно решить проблему и ни в чём себе не отказывать.  Ну у каждого свой выбор...
 
Alexey Navoykov:

Кстати да, это же ещё один баг.  Т.е. помимо раздельной инициализации статических/глобальных перемеренных,  оно ещё и сами статические переменные инициализирует в неправильном порядке. И вот это уже даже посерьёзней проблема.  Вот вы говорите "не инициализировать другие статик-переменные теми методами или функциями в которых присутствуют статик-переменные".  Как вы это соблюдать в реальности предлагаете?  Каждая функция независима, существует сама по себе. У неё своя реализация, которая может меняться. Раньше допустим в ней не было статик-переменной, а потом вы решили добавить.  И значит где-то может что-то неправильно начать работать, ибо там закладывалось на то, что в функции не будет статической переменной.  И как вы это всё контролировать собираетесь.

Да никаких проблем-то в этом нет. Достаточно отказаться от инициализации переменных функциями и всё встанет на свои мета.

int a(int n)
{
 static int f=7;
 return(f+=n);
}

void OnTick()
{
 static int b;
 b=a(9);
}
Так будет работать на-ура. В чём проблема??? В одной строке?
 
Alexey Viktorov:

Так будет работать на-ура. В чём проблема??? В одной строке?

Так а вы понимаете, что здесь логика другая получилась?  Зачем тогда вообще объявлять b статической, если вы на каждом вызове ей присваиваете значение?
 
Alexey Navoykov:
Так а вы понимаете, что здесь логика другая получилась?  Зачем тогда вообще объявлять b статической, если вы на каждом вызове ей присваиваете значение?

Согласен. В спешке чуток начудил. Но ведь переменной b может быть присвоено значение при каком-то условии и вместо 9 в функцию могут передаваться значения в зависимости от условия.


А вот в вашем примере переменная 'а' обязательно должна быть инициализирована на глобальном уровне?

template<typename T>
class A
{
 public: 
  static int a;
};

template<typename T>
int A::a=10;

int f() { return A<int>::a; }

int a= f();

void OnStart()
{
  Print(a); // Результат: 0
};

В скрипте нет другого варианта, а в советнике можно объявить переменную на глобальном уровне, а инициализировать её в OnInit()

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

Именно этот пример и нарушает рекомендацию документации о недопущении инициализации переменных функциями. Разработчикам было проще написать такое предупреждение, чем объяснять где можно, а где нельзя.

Уберите static из своего примера и получите желаемый результат.

 
Alexey Viktorov:

А вот в вашем примере переменная 'а' обязательно должна быть инициализирована на глобальном уровне?

Необязательно, но мне так удобней.  Если же это константа (а в глобальной видимости объявляются в основном константы, если код грамотный), то здесь и выбора другого нет.

В скрипте нет другого варианта, а в советнике можно объявить переменную на глобальном уровне, а инициализировать её в OnInit()

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

Именно этот пример и нарушает рекомендацию документации о недопущении инициализации переменных функциями. Разработчикам было проще написать такое предупреждение, чем объяснять где можно, а где нельзя.

Уберите static из своего примера и получите желаемый результат.

На всё, что выделенно жёлтым, у меня один вопрос:  ЗАЧЕМ?   Я уже нашёл, как решить проблему.
 
Обновил файл на прошлой странице. Исправлен небольшой недочёт.
 
Alexey Navoykov:

Необязательно, но мне так удобней.  Если же это константа (а в глобальной видимости объявляются в основном константы, если код грамотный), то здесь и выбора другого нет.

На всё, что выделенно жёлтым, у меня один вопрос:  ЗАЧЕМ?   Я уже нашёл, как решить проблему.

Вы нашли способ, как ее создать. 

 
Alexey Navoykov:

Да, верно, в обычных классах инициализируется сразу. А вот в шаблонных - нет:

У Вас происходит попытка использовать статик-поле класса на этапе инициализации до того, как был создан хотя бы 1 экземпляр этого класса. На мой взгляд, это извращение... Вот так все работает нормально:

template<typename T>
class A
{
 public: 
  A(){}
  static int a;
  int f(){return a;}
};

template<typename T>
int A::a=10;

A<int> _a;


int a= _a.f();

void OnStart()
{
  Print(a);
};
Принцип инкапсуляции вообще предполагает, что такие поля должны быть скрытыми, а не общедоступными.
Причина обращения: