none
C++/CLIでの静的な配列について RRS feed

  • 質問

  • VC2++2008のwindowsフォームアプリケーションで以下のような機能のプログラムを作成しようとしています。

    Timerコントロールを使用し,Timer_Tickイベントが起こるたびに配列の中に数値を入れていく。

    そのために,以下の様なコードを書きました。

    private: System::Void timer2_Tick(System::Object^  sender, System::EventArgs^  e) {

          array<int>^a = gcnew array<int>(150);

               a[i] = 入れたいデータ;

               i++;

    }

    上記のプログラムを実行すると,データは入っていますが, i の要素以外は全て 0 になってしまいます。

    VBAなどのように,配列を静的な変数として使用するにはどのようにすればよろしいでしょうか?

    初歩的な質問ですが,宜しくご教授お願い致します。

    2012年3月24日 4:04

回答

  • インスタンス変数にしてください。


    Jitta@わんくま同盟

    • 回答としてマーク 山本春海 2012年4月23日 8:33
    2012年3月24日 5:38
  • 質問の説明の仕方から、スコープなどに理解不足を感じたので参照先を示しましたが、こんどはサンプルを提示します。ウィザードで作成し、ボタンをstartとstop追加して、タイマーを設定・動作させています。配列の数値表示はしないので、ブレークポイントを設定して変数の中身を見てください。VS2005、Windows7 64bitでビルドし動作を確認しました(とりあえず動いたというだけ)。

    このコードで、何がどのように動作しているのか理解してから自分のプログラムをしてください。理解せずに適当にやっていくと、どんどん泥沼にはまります。他の方々が既に指摘していますが、図書館に行くとかして自分が分かる内容の書籍(<=意外と重要です)を探すことをお勧めします。インターネットで探してもいいのですが、経験的に、最初は本の方がよいです(なぜかというと、索引があるのと、検索したい単語が書籍の文章中に出てくるから)。

    それに、私のサンプルもマズイ部分があるとも限りませんから。

    #pragma once
    
    namespace TimerTestForm {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			myTimer = gcnew System::Windows::Forms::Timer;
    			a = gcnew array<int>(150);
    			i = 0;
    			myTimer->Tick += gcnew EventHandler( TimerEventProcessor );
    			myTimer->Interval = 1000;
    			myTimer->Start();
    		}
    
    	protected:
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    			if(a)
    				delete a;
    			if(myTimer)
    			{
    				myTimer->Stop();
    				delete myTimer;
    			}
    		}
    
    	private:
    		static System::Windows::Forms::Timer^ myTimer;
    		static array<int>^a;
    		static int i;
    		private: System::Windows::Forms::Button^  button1;
    		private: System::Windows::Forms::Button^  button2;
    		System::ComponentModel::Container ^components;
    
    		static void TimerEventProcessor( Object^ /*myObject*/, EventArgs^ /*myEventArgs*/ )
    		{
    			if(i >= 10)
    				i = 0;
    			a[i] += 10;
    			i++;
    		}
    
    #pragma region Windows Form Designer generated code
    #pragma endregion
    	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    				 i = 0;
    				myTimer->Start();
    		 }
    	private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
    				myTimer->Stop();
    		 }
    	};
    }
    
    

    • 回答としてマーク 山本春海 2012年4月23日 8:33
    2012年3月24日 13:26

すべての返信

  • インスタンス変数にしてください。


    Jitta@わんくま同盟

    • 回答としてマーク 山本春海 2012年4月23日 8:33
    2012年3月24日 5:38
  • ご返信ありがとうございます。

    プログラムを始めたばかりで,インスタンス変数自体もよく理解できません。

    申し訳ありませんが,もう少し具体的にお教えねがえないでしょうか?

    2012年3月24日 6:53
  • プログラムの前後関係が分からないのですがtimer2_Tick()を実行するたびにaがgcnewで毎回確保され続けているように見えます。毎回aが確保されるので、i以外は初期値としての0が入っていて、iはたった今代入したから値が入っているという気がします。

    timer2_Tick()の中ではないところでaをgcnewして、このときstaticも宣言しておけば対処できるかと思います。MSDNに参考になりそうなサンプルがありました。

    2012年3月24日 6:55
  • ご返信ありがとうございます。

    >>毎回aが確保されるので、i以外は初期値としての0が入っていて、iはたった今代入したから値が入っているという気がします。

    ご指摘のことに気づいて,配列の宣言を同じヘッダーファイル内のイベント外に記入したところ,『定義されていない識別子です。』とのエラーが出てしまいました。

    お教えいただいたリンク先のサンプルを拝見させていただきましたが,C++とC++/CLIで記述方法の違いがあるためかうまく動作しませんでした。

    まだ,勉強不足で,クラスの設計方法もよくわかりませんので,クラスを使用しない方法で解決することはできないでしょうか?

    2012年3月24日 7:35
  •  i は、どこに、どうやって宣言していますか?これは、なぜ初期化させないのでしょう?なぜ、0から始まるのでしょう?同じところで同じように宣言すればよいと思いませんか?


    Jitta@わんくま同盟

    2012年3月24日 7:49
  • ご返信ありがとうございます。

    すいません。i に関しては全く記述していませんでした。

    i は ヘッダーファイル先頭の #pragma once  と namespace プロジェクト名  の間に記入しています。0から始まります。

    その位置に配列の宣言を記入すると『定義されていない識別子です。』とのエラーが出てしまいます。

    2012年3月24日 8:01
  •  TV のクイズ番組を見ることはありますか?出題者が問題を読んでいる途中で回答者が回答権を得た場合、間違えることが多いですよね。特に、手を挙げたところで「~ですが」と聞こえた場合。問題は、最後まで聞かなければわかりません。こういう場所で情報を得るときも同じです。発生している事象、すなわち問題を、もれなく伝えなければ正しい回答は得られません。

     『定義されていない識別子です。』とエラーになるとき、定義されていない識別子は何か、ということも、表示されていませんか?そしてそれは、array<> だったりしませんか?

     C++ から離れて長いし、特にC++/CLI は触っていないので外しているかもしれませんが、今宣言しているのはグローバルな場所で、そこで宣言するようなことはやめましょう、というのが最近の流れです。必要最小限に公開するように考えましょう。

     そして、まずは、本屋に行って5~6冊立ち読みし、理解できそうなものを1~2冊購入してくることを勧めます。Amazon 等ではウェブ立ち読みというものもできますので、そういうものも利用してください。そして、1~2週間時間をもらって、まずは勉強してください。そこまで時間がとれなくても、2~3日はもらって、まずは座学で勉強してください。結果的に、そうする方が短時間でよりよいものを作れます。これは、経験則です。


    Jitta@わんくま同盟

    2012年3月24日 8:21
  • C++/CLI は C++ をある程度理解し、.NET Framework の知見があることが前提といえる言語環境です。
    C++ での経験が浅い状態ではかなり茨の道といえますが、なぜ、C++/CLI を選ばれたのでしょうか。
    (インスタンス変数の概念がわからないあたりで、C++ の経験がないか、足りないと判断しています)

    どうしても、C++/CLI で書き続けないといけないのであれば、その手の入門書籍を探して、基礎から学んでください。
    そして、クラスとは何か、インスタンス変数とは何か、C++/CLI でどう書かなければならないかを調べてください。

    // デフォルトでもインスタンス変数ありますよね。components とか。
    // まねして変数を定義しつつ、初期化はコンストラクターでやると実現できるでしょう。
    // ただ、適当にやって回避するのではなく、なぜこれでうまくいくのか、どういった書き方なのかは
    // きちんと学んでください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年3月24日 13:03
    モデレータ
  • 質問の説明の仕方から、スコープなどに理解不足を感じたので参照先を示しましたが、こんどはサンプルを提示します。ウィザードで作成し、ボタンをstartとstop追加して、タイマーを設定・動作させています。配列の数値表示はしないので、ブレークポイントを設定して変数の中身を見てください。VS2005、Windows7 64bitでビルドし動作を確認しました(とりあえず動いたというだけ)。

    このコードで、何がどのように動作しているのか理解してから自分のプログラムをしてください。理解せずに適当にやっていくと、どんどん泥沼にはまります。他の方々が既に指摘していますが、図書館に行くとかして自分が分かる内容の書籍(<=意外と重要です)を探すことをお勧めします。インターネットで探してもいいのですが、経験的に、最初は本の方がよいです(なぜかというと、索引があるのと、検索したい単語が書籍の文章中に出てくるから)。

    それに、私のサンプルもマズイ部分があるとも限りませんから。

    #pragma once
    
    namespace TimerTestForm {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			myTimer = gcnew System::Windows::Forms::Timer;
    			a = gcnew array<int>(150);
    			i = 0;
    			myTimer->Tick += gcnew EventHandler( TimerEventProcessor );
    			myTimer->Interval = 1000;
    			myTimer->Start();
    		}
    
    	protected:
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    			if(a)
    				delete a;
    			if(myTimer)
    			{
    				myTimer->Stop();
    				delete myTimer;
    			}
    		}
    
    	private:
    		static System::Windows::Forms::Timer^ myTimer;
    		static array<int>^a;
    		static int i;
    		private: System::Windows::Forms::Button^  button1;
    		private: System::Windows::Forms::Button^  button2;
    		System::ComponentModel::Container ^components;
    
    		static void TimerEventProcessor( Object^ /*myObject*/, EventArgs^ /*myEventArgs*/ )
    		{
    			if(i >= 10)
    				i = 0;
    			a[i] += 10;
    			i++;
    		}
    
    #pragma region Windows Form Designer generated code
    #pragma endregion
    	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    				 i = 0;
    				myTimer->Start();
    		 }
    	private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
    				myTimer->Stop();
    		 }
    	};
    }
    
    

    • 回答としてマーク 山本春海 2012年4月23日 8:33
    2012年3月24日 13:26
  • アドバイスありがとうございます。

    すいません。質問をする際はもっとよく内容をまとめてから行うように致します。

    C++の本は2冊ほど購入して読んだのですが,C++/CLIと全く違うものだと,後から知りました...

    まだ,C++の内容をどのようにC++/CLIに生かせるのかもわかりませんが,本を読みながら勉強していきたいと思います。

    2012年3月26日 10:25
  • ご返信ありがとうございます。

    C++を使用してGUIソフトの作成方法がわからなかったため,C++/CLIを選択しました。

    C++/とC++/CLIがほぼ違う言語のような扱いだと後からしりました。

    まずは,C++から勉強していきたいと思います。

    2012年3月26日 10:28
  • ご返信ありがとうございます。

    ご提示して下さったコードで,私が行いたいことが出来るようになりました。

    最初の質問時に,静的な配列はどのように宣言すれば良いのかだけを聞いていればよかったと思いました。

    今回は,勉強になりました。ありがとうございます。

    2012年3月26日 10:31
  • 最初の質問時に,静的な配列はどのように宣言すれば良いのかだけを聞いていればよかったと思いました。

    いやいや、そんな一行質問は逆にうまくいきませんよ。

    なぜそれを書こうとするのか、指している言葉が違うのではないか、あなたの習熟度・経験がどの程度かなど、それを確認するレスポンスがつくので、結局うまくいきません。
    今回の質問文の方がまだマシです。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年3月26日 14:21
    モデレータ
  • Azuleanさんに補足で、その聞き方ではprotecyamyamさんの求めているものが本当に「静的な配列」なのかがわかりません。(今もまだわかりませんが。)

    実際、BlueSkyColorsさんの提示されたコードは静的配列であり動作するものですが、この目的であれば静的配列の必要はなく、Jittaさんが最初に提示した「インスタンス変数」で十分です。つまり、BlueSkyColorsさんのコードは「動く」けど「正しい」わけではありません。そして正しくないコードはバグの温床になります。

    2012年3月26日 23:01
  •  重箱の隅だと思いますが、これはちょっと…という点。

    1.変数 a を150個の配列としていますが、タイマー コールバックでは10個までしか使っていません。どちらが正しいのでしょう?今回はたまたま使用する側の数が少ないですが、多かったなら・・・
    2.150個で十分だと思っていましたが、実は200個必要になりました。この時、150がマジック ナンバーとして定義されているので、何カ所直さなければならないか、わかりません。
    3.フォームを複数作って別々にカウントしたいのですが、どのフォームも同時にカウントされます。


     VBA はわかりませんが、C では、ローカル変数に static と付けることで、関数から抜けても値が保存される変数、静的変数を宣言できました。C++/CLI では、ローカル変数に static と付けることはでき・・・C# はできないのだけれど、C++/CLI はどうなんだろう?できないとすると、インスタンス変数(フィールド変数)で代用します。このインスタンス変数(フィールド変数)を static と宣言するとクラス変数になり、全てのインスタンスで値が共有されます。C のローカル変数を静的に宣言するのと同じ感覚であれば、スコープが広くなりますが、インスタンス変数(フィールド変数)を利用します。


    Jitta@わんくま同盟

    2012年3月28日 12:41
  • コード内容に関した3つの指摘なので私に対する問いかけだと思いますが、どういうふうに回答すればいいのか迷いました。そこで、指摘された内容に対して、私がどんな意図でサンプルを書いたかを説明します。

    1.10個だけ使ったのは、動作を確認するためという単純な理由です。i以外の値が0であるという説明だったので短いループにして動作確認しました(Start/Stopも動作確認のため)。10という値にも深い理由は無く、適当に設定しただけです。150という値は、質問の中にあった値をそのまま使用しました。つまり、配列長さ妥当性については配慮していません。

    2.配列長に対して何か手を打つことも考えましたが、その手法を知らない場合は、その説明もしなければならなくなります。質問者の意図している範囲から外れるので、質問者のコードになぞらえたままの方がいいと考え、そのまま記述しました。

    3.インスタンス(クラス)に対して理解をされていない方が、複数作ることは無いとも考えましたが、複数作成している可能性が高いと判断しました。理由はtimer2_Tick()と、関数名に"2"と数字が付いていたからです。だからこそ「静的」という具体的な表現を使ったのだと想定しました。つまり「フォームを複数作って別々にカウントしたい」のではなく「フォームを複数作って共通にカウントしたい」のだと解釈しました。

    追記:3.は、1つのインスタンスで複数のタイマーを使って共通にカウントしたい、というケースもあります。こちらの方が考え方としては素直ですね。

    ご指摘の3点は、確かに疑問符が出るところです(他にも、自分自身どうしようかと考えたところがありますが)。しかしこれらに対する情報が提示されていないので判断のしようがありません。仮に分からなったとしたら、それは新たな質問事項になると考えました。もっとも、問いかけて確認すればよいではないか、と指摘されると反論しにくいですが・・・。結局、唯一意識したのはdeleteの実行です。

    まぁ簡単に言うと、私のは「動くだけのサンプル」だということですね。

    これで回答になっているでしょうか。

    ちなみに、

    		void TimerEventProcessor( Object^ /*myObject*/, EventArgs^ /*myEventArgs*/ )
    		{
    			static array<int>^a = gcnew array<int>(150);
    			if(i >= 10)
    				i = 0;
    			a[i] += 10;
    			i++;
    		}

    にするとコンパイルエラー(C3145)が発生します。


    • 編集済み BlueSkyColors 2012年3月30日 21:42 3.に関して思い出したのでことを追記。
    2012年3月30日 15:17
  • 結局、唯一意識したのはdeleteの実行です。


    このタイミングで delete しては“いけません”。クラス変数なので、インスタンス破棄のタイミングでは delete してはいけません。


    Jitta@わんくま同盟

    2012年4月11日 12:42