OOPに関するヘルプ - ページ 2

 
Ihor Herasko #:
突然レナートと議論したくなったのなら、歓迎します

えー、こんな感じで。

Ihor Herasko#:

もうひとつ。オブジェクトの配列は、ポインタを介して作成するのが最適です。そうでなければ、スタック・メモリに配列ができてしまい、非常に小さくなってしまいます。

は、Renatさんのおっしゃることと、上記の例とは何の関係もありません。
 
Ihor Herasko #:

この例を少し修正して、宣言をローカルレベルにして、それほどひどくない数値を入れると、コンパイラは何が問題なのかを直接教えてくれます。

レナーテに反論することがあれば、どうぞご自由に

あなたの例では

Test *pClassTest[];

関数の外側で、グローバル変数として定義されます。では、このスタックは何なのでしょうか?ヒープである。

 
Vasiliy Sokolov #:

あなたの例では

は関数の外側でグローバル変数として定義されます。では、このスタックは何なのでしょうか?山盛りです。

驚きです。ワシリー、議論の内容を理解する努力もせず、議論に参加したのか?

Igorは両方ともヒープで議論しており、このコードの行は2番目の(「正しい」)例のもので、コンパイルしない(そしてポインタも入っていない)1番目の例とは異なり、コンパイルするものです。

 
Ihor Herasko #:

この例を少し修正して、宣言をローカルレベルにして、それほどひどくない数値を入れると、コンパイラは何が問題なのかを直接教えてくれます。

突然レナートに反論したくなったら、どうぞ

酷くない」というテーマで。言い方を教えてください。スタックはデフォルトで1MBで、その上にARRAY_SIZE*sizeof(Test)を割り当てるので、当然大きくなる。

 
Ihor Herasko #:

問題ではありませんし、ましてや潜在的な問題でもありません。MTのメモリ操作のクセが出ただけです。ここでは、静的な配列を示します。

そして、こちらがダイナミックアレイ。

この場合、すべてコンパイルして動作します。

前に進みましょう。

最初のケースでは、コンパイル時にメモリが確保される。つまり、プログラムの.dataセグメントには、これらのARRAY_SIZE*sizeof(Test)を含める必要があります。そこでARRAY_SIZEとは何だったのか))間違っていなければ、2TB以下では.ex5ファイルが出ます)))。

2番目のケースでは、ちょうどARRAY_SIZE*sizeof(Test*)バイトの山、すなわち480GBに割り当てに行きます。

 

double x[268435448]; を一般セクションでコンパイルします。

268435449はもうコンパイルできません。

そして、スクリプトのOnStart()で268435449がコンパイルされます。この巨大な配列xは一般セクションに残して おきました。

268435448*8=2,147,483,584 は2ギガです。

***

そして、ここでは、関数:double x[268435456]での制限を説明します。

 

スタックについては参考文献をご覧ください。

MQL5プログラムのスタック サイズを示し、再帰的な関数呼び出しの場合は十分なスタックサイズが必要です。

チャート上でスクリプトやExpert Advisorを起動すると、少なくとも8MBのスタックが割り当てられますが、インジケータではこのプロパティは機能せず、スタックは常に1MBに固定されています。

テスターで起動すると、常に16Mbのスタックが確保されます。


スタックは再帰的な関数呼び出しにのみ使用されると考えるべきでしょう。したがって、静的配列がどのようなサイズであっても、再帰的な呼び出しの場合はスタックがそのまま残ります。

 
Dmitry Fedoseev #:

驚きです。Vasiliyさんは、議論の内容を理解する努力もせずに、議論に参加してしまったのでしょうか?

Igorは両方ともヒープで議論しており、このコードの行は、コンパイルしない(そしてポインタがない)最初の例とは対照的に、コンパイルする2番目の(「正しい」)例のものです。

オッケーです。理解できない。わかりましたか?正確に理解できていますか?まさにその通り?

この議論は、次のような発言に集約される。

トレーディング、自動売買システム、トレーディング戦略のテストに関するフォーラム

OOPに関するヘルプ

イホール・ヘラスコ さん 2021.09.19 21:00

そしてもうひとつ。オブジェクトの配列はポインターを用いて作成するのがよいでしょう。そうしないと、スタック・メモリにごくわずかな配列が出来てしまいます。

Strategy2 *pZ[]; 

これは不正確な記述です。配列はスタックに割り当てられない。ポインタ経由でも静的でもない。これは簡単に確認できます。

2点目:この例ではポインターを使用していますが、そこではポインターは必要ありません。以下は、素板を使わない場合の相当例です。

#define               ARRAY_SIZE           int(60000000)
class Test
{
public:
   int               nA;
   double            fB;
   datetime          dtC;

                     Test(void)
                       : nA(0)
                       , fB(1.0)
                       , dtC(__DATETIME__)
                     {
                     };
};

Test pClassTest[];
 
void OnStart()
{
   if (ArrayResize(pClassTest, ARRAY_SIZE) != ARRAY_SIZE)
   {
      Alert("Not enought memory");
      return;
   }
}

同じ60,000,000個のオブジェクトが、デフォルトのコンストラクタ呼び出しで割り当てられる。また、手動で初期化してメモリを解放する必要がないため、大幅に(何倍も)時間を節約できるとともに、コードをより安全に、シンプルに、ローカルにすることができます。

確認中です。壊してプロセスマネージャで開いてみましょう。ここProcessHackerで。

プライベート領域では、それぞれ4GBと2GBが割り当てられている。

スタックサイズを確認する。


スクリプトのスタックは8MBで、これが最大スタックとなります。

頭の中でロジックを組み立てるのは簡単で、20バイトのオブジェクトが60,000個あれば、1Gバイト以上のメモリを占有する計算になる。どんなスタックでも、全部は入りきらない。

また、ポインターのあるサンプルとないサンプルで、同じプロファイリングが得られます。スタックには何も割り当てられていない。しかし、ポインターを使った例では、1GB多くメモリを割り当てているので、それも簡単にわかる。

実行時にスタックの最大サイズを変更することはできないことに注意してください。最大スタックサイズはあらかじめ定義され、コンパイル時などに設定される。

s.s. 純粋に理論的には、スタック上に小さな静的配列を割り当てることは可能です(mqlでは明示的に行われません)。しかし、これは大きな問題につながります。なぜなら、ユーザーは、たとえ異なるリンクされた関数であっても、このような配列を簡単に複数作成し、スタックをオーバーフローさせてしまう可能性があるからです。だからこそ、ユーザーはそのような自己満足から保護され、サイズに関係なく常に 共有メモリに配列を割り当てる必要があるのです。

 
Dmitry Fedoseev #:

double x[268435448]; を一般セクションでコンパイルします。

268435449はもうコンパイルできません。

そして、スクリプトのOnStart()で268435449がコンパイルされます。この巨大な配列xは一般セクションに残して おきました。

268435448*8=2,147,483,584 は2ギガです。

***

そして、ここでは、関数:double x[268435456]での制限を説明します。

これらは、コードアナライザーの限界です。mqlのメモリがあれば4Gbや6Gbや20Gbを割り当てても特に問題はない。

 
Vasiliy Sokolov #:

は、サイズに関係なく常に 共有メモリに配列を確保します。

静的配列を使用しない?