mql5におけるOOP、テンプレート、マクロ、微妙な使い分け - ページ 2

 
Ilya Malev:

グローバルに初期化されるクラス(OnInitの前)でstaticフィールドを宣言する場面は多々ありますが、クラスの記述直後、そのインスタンスのグローバル変数を 宣言する前にstaticフィールドを再宣言すれば、staticフィールドの初期化に問題はありませんでした(この場合、グローバルとみなされるので、クラスインスタンスの前に初期化すると理解されているからです)。だから、メソッドや関数の内部でスタティック変数を宣言するのをやめれば、まったく問題はない。

そうですね、通常のクラスではすぐに初期化されます。しかし、テンプレートのものでは初期化されない。

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変数が初期化されますが、int a(int n)関数内のstatic int f変数はまだ初期化されないので、結果的にちんぷんかんぷんな状態になります。

つまり、スタティック変数とグローバル変数が別々に初期化されるだけでなく、スタティック変数 自体も誤った順序で初期化されてしまうのです。つまり、「スタティック変数を含むメソッドや関数で他のスタティック 変数を初期化しない」ということですが、実際にはどのように守っていけばいいのでしょうか? それぞれの関数は独立して 存在しています。独自の実装を持ち、変更される可能性があります。 今までスタティック変数がなかったのに、それを追加することになったとします。つまり、どこかで何か問題が起こるかもしれないということです。 そして、このすべてをどのようにコントロールするのでしょうか?

 
このように、あれもこれも諦めるという判断が理解できないのです。なぜあきらめるのか、何のために? 問題を解決し、自分を否定しないためだ。 まあ、人それぞれの選択だが......。
 
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の代わりに条件に応じて関数に値を渡すことができる。


しかし、あなたの例では、'a' 変数はグローバルに初期化されなければならないのでしょうか?

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
};

スクリプトには他のオプションはなく、Expert Advisorでは、グローバルレベルで変数を宣言し、OnInit()で初期化することが可能です。

初期化シーケンスを理解し、観察できれば十分です。まずグローバル変数、次にスタティック変数、そしてコードに登場するローカル変数。

この例はまさに、ドキュメントで推奨されている「関数で変数を初期化 しない」ということに違反しています。開発者にとっては、できるところとできないところを説明するよりも、このような警告を書く方が簡単だったのです。

例から静的なものを取り除き、目的の結果を得ることができます。

 
Alexey Viktorov:

しかし、サンプルの変数 'a' はグローバルに初期化されなければならないのでしょうか?

必ずしもそうではありませんが、その方が便利です。 もしそれが定数なら(そしてグローバルに見える定数は、コードがスマートであれば、ほとんど宣言されています)、他の選択肢はありません。

スクリプトには他のオプションはなく、EAではグローバルに変数を宣言し、OnInit()で初期化することが できます。

初期化シーケンスを理解し、観察できれば十分 です。まずグローバル変数、次にスタティック変数、そしてコードに登場するローカル変数。

この例はまさに、ドキュメントで推奨されている「関数で変数を初期化 しない」ということに違反しています。開発者にとっては、できるところとできないところを説明するよりも、このような警告を書く方が簡単だったのです。

あなたの例から静的なものを取り除き、望ましい結果を得ることができます。

黄色いものについては、「なぜ?問題を解決する方法はすでに見つかっています。
 
前ページのファイルを更新しました。軽微な不具合を修正しました。
 
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);
};
一般的なカプセル化の原則では、このようなフィールドは公開ではなく、隠蔽されるべきとされています。