none
C++のクラスの中で動的メモリ確保をした配列の値を、同じクラス内の別の関数で参照したい。 RRS feed

  • 質問

  • つい最近C++のクラス・オブジェクトを覚えた者です。
    若干長めですがよろしくお願いします。

    クラスの中の関数で動的にメモリ確保をした配列の値を、
    同じクラス内の別の関数で参照したいと思っています。

    〔プログラム文の特徴〕
    ・言語はC++で、クラスとオブジェクトを使っています。
    ・クラスの中で動的にメモリを確保して、二次元配列を作っています。
    (原コードを下に貼り付けています)

    〔やりたいこと〕
    動的メモリ確保により配列を定義して値を入力した関数 a()
    とは別の関数 b() に値を参照させたいです。

    〔原コード〕

    #include <stdio.h>

    class CLS_PRACTICE{

    public:
    CLS_PRACTICE();
    ~CLS_PRACTICE();
    void a();
    void b();

    private:
    int x0,y0;
    int bb,cc;
    };

    CLS_PRACTICE::CLS_PRACTICE() //コンストラクタ
    {
    }

    CLS_PRACTICE::~CLS_PRACTICE() //デストラクタ
    {
    }

    void CLS_PRACTICE::a()
    {
    printf("x0 を入力して下さい:");
    scanf("%d",&x0);

    printf("y0 を入力して下さい:");
    scanf("%d",&y0);

    puts("");

    int **HAIRETSU = new int *[y0]; //配列のメモリを動的確保
    for(int y = 0; y < y0; y++)
    HAIRETSU[y] = new int [x0];

    for(int y = 0; y < y0; y++){ //配列の各値を計算・表示
    for(int x = 0; x < x0; x++){
    HAIRETSU[y][x] = 100 * y + x;
    printf("HAIRETSU[%d][%d] = %d \n", y, x, HAIRETSU[y][x]);
    }
    }
    }

    void CLS_PRACTICE::b()
    {
    // bb = HAIRETSU[y0-1][x0-1];
    // cc = bb * 2;
    //すぐ上の二行のコメントアウトをなくすとエラーになる
    }

    void main()
    {
    CLS_PRACTICE *OBJ_PRACTICE = new CLS_PRACTICE;

    OBJ_PRACTICE -> a();
    OBJ_PRACTICE -> b();

    delete OBJ_PRACTICE;
    }

    〔質問〕
    『関数a() で定義した配列の値を、関数b() でも参照させる』ために、

    方法1.関数をa(),b()に分けずに1つにまとめる。
    方法2.ポインタを使う。
    “これら2つ以外”に方法はないでしょうか?
    (諸事情あり、これらは避けたいのです)

    また、静的にメモリ確保をする場合なら、配列「HAIRETSU」をクラスのメンバ変数にすれば良いことは分かるのですが、
    ここでは動的に確保していますので、メンバ変数に「HAIRETSU」を追加すべきなのか、
    すべきならどのように記述すべきなのかが調べても分かりませんでした。

    ↓足りない情報があればお知らせいただければと思います。

    〔端末環境〕
    OS:Win7 Professional SP1
    bit数:64
    メモリ:16GB
    CPU:4コア 8スレッド 3.4GHz

    〔コンパイラ・バージョン〕
    Visual Studio 2012 Express for Windows Desktop

    よろしくお願いします。

     

    2013年2月5日 0:54

回答

  • 〔質問〕
    『関数a() で定義した配列の値を、関数b() でも参照させる』ために、

    方法1.関数をa(),b()に分けずに1つにまとめる。
    方法2.ポインタを使う。
    “これら2つ以外”に方法はないでしょうか?
    (諸事情あり、これらは避けたいのです)

    方法1を回避するのはいいのですが、方法2を回避するというのはどういう意味でいってますでしょうか?
    #main()内でa,bの受け渡しをしたくないとか(関数の引数にしたくない)とかかなと思いますが…

    とりあえず、方法2の回避はおいといて、最初の質問時のソースをいじるとすると

    #include "stdafx.h" #include <stdio.h> class CLS_PRACTICE{ public: CLS_PRACTICE(); ~CLS_PRACTICE(); void a(); void b(); private: int x0,y0; int bb,cc; int **HAIRETSU; //メンバにしてOK }; CLS_PRACTICE::CLS_PRACTICE() //コンストラクタ { HAIRETSU = NULL; //NULLをいれておく(メモリ確保の確認につかえるので) } CLS_PRACTICE::~CLS_PRACTICE() //デストラクタ { if ( HAIRETSU != NULL )//解放確認
    { for(int y = 0; y < y0; y++){ { delete[] HAIRETSU[y]; } } delete[] HAIRETSU; HAIRETSU = NULL; } } void CLS_PRACTICE::a() { if ( HAIRETSU != NULL )return; //すでにメモリ確保してた場合の回避 printf("x0 を入力して下さい:"); scanf("%d",&x0); printf("y0 を入力して下さい:"); scanf("%d",&y0); puts(""); if ( x0 < 1 || y0 < 1 )return; //サイズ確認 HAIRETSU = new int *[y0]; //配列のメモリを動的確保 for(int y = 0; y < y0; y++) HAIRETSU[y] = new int [x0]; for(int y = 0; y < y0; y++){ //配列の各値を計算・表示 for(int x = 0; x < x0; x++){ HAIRETSU[y][x] = 100 * y + x; printf("HAIRETSU[%d][%d] = %d \n", y, x, HAIRETSU[y][x]); } } } void CLS_PRACTICE::b() { if ( HAIRETSU == NULL )return; //メモリ確保してない場合の回避 bb = HAIRETSU[y0-1][x0-1]; cc = bb * 2; } void main() { CLS_PRACTICE *OBJ_PRACTICE = new CLS_PRACTICE; OBJ_PRACTICE -> a(); OBJ_PRACTICE -> b(); delete OBJ_PRACTICE; }

    こんな感じでどうでしょうか?
    • 回答としてマーク t_yanagi 2013年2月11日 21:32
    2013年2月7日 3:42

すべての返信

  • 方法は適材適所でいくらでもあります。

    • HAIRETSUはいつメモリ解放するのか、ついでに言えば適切な解放タイミングを考慮した場合、それに対応する適切な確保タイミングはa()内で正しいのか
    • メンバー関数a()とb()の関係。a()とb()は独立して呼ばれるのか、a()が複数回呼ばれたとき、b()はどちらのa()が確保したHAIRETSUを参照したいのか

    などなど考慮する点が多数あります。

    • 回答としてマーク t_yanagi 2013年2月5日 8:22
    • 回答としてマークされていない t_yanagi 2013年2月11日 21:14
    2013年2月5日 1:23
  • 返信誠に有難うございます。

    > HAIRETSUはいつメモリ解放するのか、ついでに言えば適切な解放タイミングを
    > 考慮した場合、それに対応する適切な確保タイミングはa()内で正しいのか

    HAIRETSUのメモリ解放はプログラム終了時です。
    (main関数の最後の方)

    先にお送りしたソースコードでは伝わりづらかったと思いますので、
    実際にやりたい内容に近くなるように作り直したソースコードを下の方に貼り付けています。

    それに伴って、関数をa(),b(),c()の3種類にしました。

    〔関数の役割〕
     a():配列のためのメモリを、動的に確保する。
        配列の全成分の初期値を計算後、入力する。
     b():配列の成分の一部を再計算後、更新する。
     c():配列「HAIRETSU」成分の一部の合計値を算出する。

    〔関数呼び出しの流れ〕
    関数 a() は、プログラム実行の初期に一度処理が終われば呼び出されることはありません。
    その後、b()により配列成分の値を変えて、c()により変更後の値を元に演算を行います。
    b()とc()の処理は、main関数で指定した n0 回分、順番に繰り返されます。

    (これで次の2行のご質問にもお答えできていると思います)

    > メンバー関数a()とb()の関係。a()とb()は独立して呼ばれるのか、a()が複数回呼ばれたとき、
    > b()はどちらのa()が確保したHAIRETSUを参照したいのか


    なお、今回貼り付けているソースコードも、
    先にお送りしたものと同様でそのまま計算するとエラーになり、
    a(),b(),c()の内容を1つの関数にまとめてしまえば計算できるようになることを確認済みです。
    (ただし、まとめてしまうと、実際のソースコードでは1つの関数が
     やたらと冗長になってしまうため、できることならそれは避けて、
     関数を跨いでも配列の値が参照できる方法を知りたいです)

    以下、ソースコードです。

    //----------------

    #include <stdio.h>

    class CLS_PRACTICE{

    public:
     CLS_PRACTICE();
     ~CLS_PRACTICE();
     void a();
     void b();
     void c();

    private:
     int  x0,y0;
     int HAIRETSU_1_sigma;  //関数cで使用
    };

    CLS_PRACTICE::CLS_PRACTICE()
    {
     HAIRETSU_1_sigma = 0;
    }

    CLS_PRACTICE::~CLS_PRACTICE()
    {
    }

    void CLS_PRACTICE::a()
    {
     printf("x0 (3以上)を入力してください:");
     scanf_s("%d",&x0);

     printf("y0 (3以上)を入力してください:");
     scanf_s("%d",&y0);

     puts("");


     int **HAIRETSU = new int *[y0];
     for(int y = 0; y < y0; y++)
      HAIRETSU[y] = new int[x0];

     for(int y = 0; y < y0; y++){
      for(int x = 0; x < x0; x++){
       HAIRETSU[y][x] = 100 * y + x;
      }
     }
    }

    void CLS_PRACTICE::b()
    {

     for(int y = 1; y < y0 - 1; y++){
      for(int x = 1; x < x0 - 1; x++){
       HAIRETSU[y][x] = HAIRETSU[y][x] / 2;
      }
     }
    }

    void CLS_PRACTICE::c()
    {
     HAIRETSU_1_sigma = 0;


     for(int x = 0; x < x0; x++){
      HAIRETSU_1_sigma += HAIRETSU[1][x];
     }

     printf("HAIRETSU_1_sigma = %d \n",HAIRETSU_1_sigma);
    }

    void main()
    {
     int n0;
     printf("関数 b,c の繰り返し回数を入力してください:");
     scanf_s("%d",&n0);

     puts("");

     CLS_PRACTICE *OBJ_PRACTICE = new CLS_PRACTICE;

     OBJ_PRACTICE -> a();

     for(int n = 1; n <= n0; n++){
      OBJ_PRACTICE -> b();
      OBJ_PRACTICE -> c();
     }

     puts("");

     delete OBJ_PRACTICE;
    }

    //----------------

    また、私事ですが、水曜朝から土曜夜までPCが使えなくなるため、
    折角返信を頂けてもすぐに反応ができないかも知れませんので、
    ご了承頂きたいと思います。

    長文になってしまい申し訳ありませんが、何卒よろしくお願い致します。

    2013年2月5日 8:23
  • 関数 a() は、プログラム実行の初期に一度処理が終われば呼び出されることはありません。

    a()のようなpublicメンバー関数はいつ何時、何度でも呼び出せるべきものです。場合によってはa()より前にb()が呼び出すことも可能です。一度きりの処理であればコンストラクターにすべきでしょう。コンストラクターであれば1度きりですし、確実にb()より先に呼ばれます。そして

    HAIRETSUのメモリ解放はプログラム終了時です。
    (main関数の最後の方)

    ということであればコンストラクターと対応してデストラクターで解放するとよいでしょう。

    …という方針でいくなら、HAIRETSUはメンバー変数として宣言することになります。

    • 回答の候補に設定 佐祐理 2013年2月11日 22:47
    2013年2月5日 9:02
  • 個人的には、もしx0やy0が固定と決まっているのならconstメンバにして、コンストラクタの初期化リスト「")"後、":"で始まって"{"まで」でガチっとはめておきたいところですね。(同一オブジェクト内であとで変更可能性がある場合は、メンバでのconst化はできませんが)

    CLS_PRACTICEはclassで宣言されている以上クラスであることはわかるのと、大文字だけというのは、慣例的にCでもC++でもdefineで使われる危険性があるので、まぜこぜにした方が安全かもしれない・・・という気はします。

     CLS_PRACTICE *OBJ_PRACTICE = new CLS_PRACTICE;

    から

     delete OBJ_PRACTICE;

    に関しては、コード上分岐ポイントがないので、この程度のサイズのオブジェクトならばnewではなくスタック変数指定で問題ないように思います。(実際はコンストラクタやa()でのnewでC++例外でプログラム終了の可能性は残ってはいますが、対処コードを書いていない場合そこで終了となるのでこの場合考慮しません)


    また、関数c()でしか使わない変数をメンバに加えるよりかは、その関数にポインタとして渡すか、戻り値で取得する、という風に狭める方が理想的に思います。( HAIRETSU_1_sigma変数 )

    そうなると、関数cも関数bもconstメンバ関数にすることが可能です。


    そして、HAIRETSUの解放処理を書かない場合、これがリークします。そこで関数aをコンストラクタに埋め込むことで、コンストラクタで2次元配列を作成・初期化し、デストラクタで解放・・・という仮定をしてみます。(配列データの数値の設定も別々にするべきなのかもしれませんが、一旦同じ場所にしておきます)

    あと、出来ればサイズが処理系依存の「int」型は個人的には直接使用を避けたい、と思っています。(といっても、これらコードの場合はまず問題はないとは思いますが)

    privateにtypedefする、というのもいいですが、別の型でも簡単に使えるようにもできるので、templateを使ってしまう、というのも一つの手です。その点はtemplate使った上にtypedefしてもいいですし、その場合は別の目的を見ている可能性が高いですが


    仕様的には確かvoid main()はint main(void)がただしかったと思います。(VC++でまず問題なく動作はするでしょうが)

    以上を踏まえて、さらにバッファ管理・演算用の処理は、入出力系統とは独立・・・というほうがいいと思うので、私だったらこんな感じにするかも、という案です。

    #include <stdio.h> 
    #include <stdint.h>
    
    #define null nullptr
    #define fixptr auto* const
    
    ///////////////////MyPractice<T,I>/////////////////// 
    
    template <class DataType, class XYIndex> 
    class MyPractice { 
    
    	DataType** data_2d;
    	const XYIndex cx_, cy_;	
    
    public: 
    
    	MyPractice( const XYIndex cx, const XYIndex cy ) : data_2d( null ), cx_( cx ), cy_( cy ) {
    
    		data_2d = new DataType*[cy];
    
    		for( XYIndex y = 0; y < cy; ++y ){
    			fixptr d = data_2d[y] = new DataType[cx];
    			for( XYIndex x = 0; x < cx; ++x ) d[x] = 100 * y + x; 
    		}
    
    	} 
    	
    	~MyPractice(){ 
    		for( XYIndex y = 0; y < cy_; ++y ) delete [] data_2d[y];
    		delete [] data_2d;
    	} 
    	
    	void b() const; 
    	DataType GetSigma() const;
    
    };
    
    ///////////////////MyPracticeの非インライン指定実装///////////////////
    
    template <class T, class Index>
    void MyPractice<T,Index>::b() const { 
    
    	const auto cx_1 = cx_ - 1; 
    	const auto cy_1 = cy_ - 1; 
    	fixptr* const data__ = data_2d; 
    
    	for( Index y = 1; y < cy_1; ++y ){ 
    		fixptr d = data__[y]; 
    		for( Index x = 1; x < cx_1; ++x ) d[x] /= 2;
    	}
    
    }
    
    
    template <class T, class Index> 
    T MyPractice<T,Index>::GetSigma() const {
    	const T* const d = data_2d[1]; 
    	T sum = 0;
    	for( Index x = 0; x < cx_; x++) sum += d[x]; 
    	return sum; 
    } 
    
    ///////////////////main/////////////////// 
    
    int main( void ){
    
    	int n0;
    	printf("関数 b,c の繰り返し回数を入力してください:"); 
    	scanf_s("%d",&n0); 
    	puts(""); 
    
    	typedef uint32_t IndexType;
    	typedef int32_t DataType; 
    	
    	IndexType cx, cy; 
    	
    	printf("x0 (3以上)を入力してください:"); 	
    	scanf_s("%u",&cx);
    	
    	printf("y0 (3以上)を入力してください:"); 	
    	scanf_s("%u",&cy);
    
    	puts(""); 
    	
    	MyPractice< DataType, IndexType > prac( cx, cy ); 
    	
    	for(int n = 1; n <= n0; n++){ 
    		prac.b();	
    		printf("HAIRETSU_1_sigma = %d \n", prac.GetSigma() );
    	} 
    	
    	puts(""); 
    	return 0;
    
    }

    stdint.hが使えない環境であればuint32_tやint32_tなどはunsigned int や intなどに適当に置き換えてください。

    「auto」が型推論の意味合いで使えない環境では、単にその変数が右辺と同じ型になるように書き換えてください。

    nullptrが使えない環境では

    #define null nullptr

    #define null NULL

    と書き換えてください。

    ※VC++2012ではいずれも使えるはずですが

    実際には、アロケータ(new deleteの方式)を指定できるようにもしたいですし、一括処理は関数オブジェクトを指定してラムダ式でサクサク・・・なんかもしたいところですし、この程度のことであれば(もうちょっと込み入ったことでも、よほどパフォーマンスを求めなければ)STLとかでいいんじゃ?という気もしますし(練習ということであれば「やるべき」、かもしれませんが)

    複数個所から使うならコンストラクタやデストラクタも非インライン実装の方がいい可能性も高いですし、この方式の2次元データだと、キャッシュの関係が微妙なので、私だったら本当は一次元配列にしておいてインデックス操作で2次元っぽく扱うだろうなと思います。

    その辺はご自由に変えてみてください。

    constメンバ関数内でメンバ変数をローカル変数に読み直していますが、そうしないと規格ではこれは最適化ありの場合でも毎回読み込まないといけなくなるため、無駄なコストがかかる可能性があるためです。(ただし、私の上記コードではpracがローカル変数になっている上にインライン化可能な状況なので、もしかしたらこの限りではないアセンブリになるかもしれませんが、別段読み直してもほぼ損はないと思います。)

    • 編集済み mr.setup 2013年2月5日 15:22
    2013年2月5日 13:41
  • 言われていることはどれも正しいことですが、相手のレベルに合わせないと何も通じないと思います。いきなりtemplateやautoやconst教を持ち出すのはどうかと。
    ちなみにmr.setupさんは「つい最近C++のクラス・オブジェクトを覚えた」レベルでtemplateを使いこなしていたのですか?

    # 個人的には#defineは反対。

    2013年2月6日 0:35
  • >個人的には#defineは反対。

    確かに、考えようによってはごもっともですw

    #defineは最終段階では外すというのも選択です。しかし、defineは単純なテキスト置換なわけですから、VC++のテキスト置換機能と相性がよく、仕掛けておけば直す場合も、使わないように修正する場合も、簡単にリファクタリングできるというメリットがあります。(置き換え先を変えるのも、数百ファイル程度なら、大抵数秒程度でできる)なので、それを込みで考えれば適宜使った方が効率はいい、ということは言えると思います。


    プログラミング初期のころの学習手順ですが

    私は明解C言語 入門編を読むのに3日ほどかけましたが、その状態からほどなく触ることになったのがC言語ではなくC++/CLIでした。もちろん最初の瞬間はまったくわからなかったのですが、とにかくわからないことがあれば片っ端から調べました。

    そしてそこから逆算していって、ネイティブC++を知り、Cの動作を確認し、C++/CLIと並行してやっていって、当時のC++/CLIの、単純な面倒さ・調べることの面倒さにorzとなって、ネイティブメインに切り替え、そのあとC#を知って、C++/CLIの効率的な調べ方を知って、そのころにC++11がいい感じだったので、やっぱ.Net Frame workとの連携が必須でない場合は、C++系統はネイティブでいっか、という状況に落ち着いてるのが現状です。

    (なので、「template」に限っていうと、ネイティブサイドなので、だいぶ後の方で学ぶことに、結果としてなりました。)

    とりあえず、人によって知ってることなんて千差万別で、聞く前に判断することはできぬのですし、いきなりガンって思いっきり色々詰め込まれてるような状況にあっても(私がいきなりC++/CLIに突入したように)十分な情熱とそれなりの時間があれば

    手順は割とどんな風でもよく、最終的に知りたいことは全部理解可能になるんだろう、と私は考えています。(分からないことがあれば、それらを逐次知っている人に聞くとか調べることができさえすればいいわけですから)


    ----

    ちなみにローカル変数・引数までconstをつけたがるのはconst教を超え「const狂」と呼ばれるらしいですねw

    まぁ呼び方とか分類するとかしないとかなんでもいいと思います。私はその方がメンテとか把握とかが楽なので、特に複雑とか変数の多い関数では好んでやっている、というだけですから、実際には、人それぞれ楽なようにやればいいと思います。(チームの方針としてconstを使わない、というのがあったらそうせざるを得ませんし)

    • 編集済み mr.setup 2013年2月6日 12:17
    2013年2月6日 11:52
  • 〔やりたいこと〕
    動的メモリ確保により配列を定義して値を入力した関数 a()
    とは別の関数 b() に値を参照させたいです。

     私は、この「やりたいこと」が、どのようにしてコードに変わったのか、というところに興味があります。あるいは、どうして書いてあるコードで出来ると思ったのか、という理由とか。

     先日、『できるプログラマになる!”伝える”技術』(amazon.co.jp)という本を読みました。この中では、プログラムを「コンピュータに対する説明である」としています。私は、「その通りである」と思いました。

     最初の投稿のような、「何故これで出来ると思ったのか」について書かれていない質問に対して、私はよく、「コードを説明して下さい」とか、「どの様にすれば出来ると考えましたか」と尋ねます。コードの一行一行について、何をしているか、理解できているでしょうか。コードがしていること、つまりあなたがコンピュータに対してコードという形で行った説明は、あなたがコンピュータにさせたいことと一致しているでしょうか。それを検証してから投稿する方が、検証せずにコードだけ投稿するよりも、はるかに短い時間で解決に至ることが出来ると、ご存知ですか?

     今回の場合、「コードにさせていること」に加えて、コード中に書いた変数の有効期間、参照可能範囲という知識も必要です。それを決めるためには、どの様な時に、どの様な場所から参照させたいか、ということが抽出できていなければなりません。この様な事を抽出して、決めるべきことを決める作業を「設計」といいます。

     まず、設計して下さい。設計できたら、設計したものを検証して下さい。

    …このように書くと、多くの人が、「設計している時間がない」とか、「後から後から変わるから、設計するだけ無駄」と返してきます。時間がないなら作って下さい。変わるなら、変わる端から設計も変えて下さい。そうすると、時間がかかるように思われますが、この様な場所で尋ねたり、コードの書き直し等を行う時間が減り、全体としては短時間で仕上げることが出来るようになります。


    Jitta@わんくま同盟

    2013年2月6日 12:44
  • 本筋でないところで申し訳ありません。

    恐れ入りますが、この投稿に対して何かあれば、別途スレッドを立ててくださいますよう、お願いいたします。
    理由としては、回答行為に対する指摘・意見交換はこのスレッドにおける本流ではないので、その方向で伸ばすのであれば、スレッドを分けた方がよいと考えるからです。

    とりあえず、人によって知ってることなんて千差万別で、聞く前に判断することはできぬのですし、

    質問文、提示されるサンプル、追加の受け答えからある程度の推測は可能です。
    今回は困っているとしてコードが貼り付けられていますが、template を事例として出せる状況にあるとは、私には思えませんでした。(失礼な物言いですみません)

    (私がいきなりC++/CLIに突入したように)十分な情熱とそれなりの時間があれば手順は割とどんな風でもよく、最終的に知りたいことは全部理解可能になるんだろう、と私は考えています。(分からないことがあれば、それらを逐次知っている人に聞くとか調べることができさえすればいいわけですから)

    そうできる人もいるし、そうできない人もいると思います。
    個人的には、そのようにできる人はこの手の質問はしないと思います。
    (独力で結構いけるタイプであることの方が多いので)

    C++ 初心者と見受けられる方に、より高いレベルを求める要素を盛り込んだ事例はやりすぎかなと個人的には思います。
    前述のように力量、理解度はある程度推測できるわけですから、そこから手を伸ばせば届く範囲のサンプルにしておかないと、余計に混乱させて解決が遠くなって、質問者・回答者の双方に悪影響を与えたり、場合によってはスルーされたりしますよ。(必要以上に長い文もスルーされることがあるので注意)

    回答側に回るのであれば、フォーラム内の他のスレッドも読んでみて、どういったものかを知ってみることも必要かもしれません。ぜひ一度どういった受け答えがあるものなのか見てみてください。

    2013年2月6日 13:59
    モデレータ
  • これだけのことを伝えるために別スレッド…というのは微妙だっと思ったので、こちらに失礼します。

    t_yanagiさんの別スレッド

    64bitOSのパソコンで、メモリ2GB以上必要なC++のプログラムをコンパイル・実行できません

    を見て、私は、プログラミングを初めて1か月程度という状況で、それなりにC++っぽい見た目のコードを用意しつつ、MSDN VC++フォーラムに投稿したt_yanagiさんの度胸、あるいは行動力を見込んで「この感じはいけるかもな」と思ったので、上記サンプルを提示しました。1か月といっても、最大では700時間以上の時間があるわけですから、どんな配分で何をやったか、人によりけりです。

    C++といえばやっぱりSTLは標準だと思いますが、STLはtemplateふつうに使ってます。自分で書く必要はないかもしれませんが、C++が必要なのであれば、どのみちtemplateの知識はそのうちそれなりの確率で使うでしょう。(なにもBoostとかLokiレベルを書けるように・・・などとは要求してないですし)

    分からないことがあってスルーする場合は「ありきたり」、とことん聞いてものにしようという意志があって聞いてくれれば「将来が楽しみ」「しかもかなり早くそうなる可能性がある」そういうつもりで返信しました。後者の場合、何か達成したい偉大な目的がある可能性もあります。(私はどうしてもやりたいことがあったのでプログラミングをはじめることになりました。)

    したがって、私が提示したサンプルについて、t_yanagiさんがわからない事を提示していただければ、答えられる範囲で答えましょう。もちろん、参考書とかである程度は確認しつつ、としていただくのが理想ですが。

    t_yanagiさんは

    >水曜朝から土曜夜までPCが使えなくなるため、

    とのことで、また、私が使える時間にも限りがありますので、次の土日までで収まりきる程度にしていただけるとありがたいです。

    2013年2月6日 14:51
  • クラス・オブジェクトを覚え始めたばかりという事で多分手探り状態なのだろうと思います。

    クラスを作る時にそのクラスにどんな役割をさせたいのかとかそういう方向で考えて
    クラスを組み立てるとその為にはどう言うメンバー変数が必要でどう言うメソッドを用意すべきか
    という部分が整理しやすくなると思いますよ。

    ただ漠然とクラスを作ってしまうと単なる関数の寄せ集めになってしまいます。
    それだとクラスとして作る意味が薄いかなと思います。
    まあ、状況によっては関数の寄せ集め的なものと作ることもありますが、
    それは必要に駆られてそうなるだけなので基本はこういうことをするクラスを作成する
    という風に考えた方がよいと思いますよ。

    練習用のプログラムみたいなのでどう言うクラスにするとか考えにくいと思いますけれど、
    何か簡単なものでよいのでどう言うことをするクラスを作るという方向で考えてみてはどうでしょうか。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。

    2013年2月7日 0:20
  • 〔質問〕
    『関数a() で定義した配列の値を、関数b() でも参照させる』ために、

    方法1.関数をa(),b()に分けずに1つにまとめる。
    方法2.ポインタを使う。
    “これら2つ以外”に方法はないでしょうか?
    (諸事情あり、これらは避けたいのです)

    方法1を回避するのはいいのですが、方法2を回避するというのはどういう意味でいってますでしょうか?
    #main()内でa,bの受け渡しをしたくないとか(関数の引数にしたくない)とかかなと思いますが…

    とりあえず、方法2の回避はおいといて、最初の質問時のソースをいじるとすると

    #include "stdafx.h" #include <stdio.h> class CLS_PRACTICE{ public: CLS_PRACTICE(); ~CLS_PRACTICE(); void a(); void b(); private: int x0,y0; int bb,cc; int **HAIRETSU; //メンバにしてOK }; CLS_PRACTICE::CLS_PRACTICE() //コンストラクタ { HAIRETSU = NULL; //NULLをいれておく(メモリ確保の確認につかえるので) } CLS_PRACTICE::~CLS_PRACTICE() //デストラクタ { if ( HAIRETSU != NULL )//解放確認
    { for(int y = 0; y < y0; y++){ { delete[] HAIRETSU[y]; } } delete[] HAIRETSU; HAIRETSU = NULL; } } void CLS_PRACTICE::a() { if ( HAIRETSU != NULL )return; //すでにメモリ確保してた場合の回避 printf("x0 を入力して下さい:"); scanf("%d",&x0); printf("y0 を入力して下さい:"); scanf("%d",&y0); puts(""); if ( x0 < 1 || y0 < 1 )return; //サイズ確認 HAIRETSU = new int *[y0]; //配列のメモリを動的確保 for(int y = 0; y < y0; y++) HAIRETSU[y] = new int [x0]; for(int y = 0; y < y0; y++){ //配列の各値を計算・表示 for(int x = 0; x < x0; x++){ HAIRETSU[y][x] = 100 * y + x; printf("HAIRETSU[%d][%d] = %d \n", y, x, HAIRETSU[y][x]); } } } void CLS_PRACTICE::b() { if ( HAIRETSU == NULL )return; //メモリ確保してない場合の回避 bb = HAIRETSU[y0-1][x0-1]; cc = bb * 2; } void main() { CLS_PRACTICE *OBJ_PRACTICE = new CLS_PRACTICE; OBJ_PRACTICE -> a(); OBJ_PRACTICE -> b(); delete OBJ_PRACTICE; }

    こんな感じでどうでしょうか?
    • 回答としてマーク t_yanagi 2013年2月11日 21:32
    2013年2月7日 3:42
  • 皆様

    反応が遅くなってしまい申し訳ありません。
    皆様ご回答有難うございます。
    ご配慮頂いて、私のレベルに合わせて教えて頂けたことで解決できました。

    今回、配列(この場合、正確には“ポインタへのポインタ”と言うんですね・・・)
    をメンバ変数として宣言するとエラーが出なくなりました。
    (一度、変数名の前に“**”を入れてメンバ変数宣言してもエラーになった記憶があり、
     他の方法があるのだろうか?と思っていたのですが、勘違いしていたのだと思います。
     お騒がせして申し訳ありません。)

    たくさん頂いたご回答の中には消化し切れていない部分もありますが、改めてチャレンジしたいと思っています。

    今後ともよろしくお願いします。

    2013年2月11日 22:02
  • yominetさん

    有難うございます。
    今回この方法で解決できました。

    > 方法1を回避するのはいいのですが、方法2を回避するというのはどういう意味でいってますでしょうか?

    すみません。質問文が正確ではないことが、後で分かりました。
    コード内のHAIRETSUを、私は「配列」と表現していましたが、正確には“ポインタへのポインタ”と言うんですね。
    ただ、“配列のように扱える”というだけで・・・。
    ポインタを使っていながら「ポインタを使う方法は避けたい」と言っているため、混乱させてしまったのだと思います。
    (質問の意図は、
     「メモリを動的確保するための“ポインタへのポインタ”を1つ使う以外には、
     ポインタを使わないようにしてコーディングしたい」ということでした。)

    2013年2月11日 22:04
  • PATIOさん

    > こういうことをするクラスを作成するという風に考えた方がよいと思いますよ。

    有難うございます。
    今回のコーディングで、この大切さが身に染みています。
    曲がりなりにもやっているつもりなものの、進めている間にだんだんぼやけてしまうため、
    コーディングを始める前だけでなく、作っている途中で適度に手を止めて、
    「やりたいことは何か」
    「何にどこでどんな役割をさせるのか」
    「これでやりたいことは出来るのか」
    と考えるタイミングをとるようにしたいと思います。

    2013年2月11日 22:04
  • mr.setupさん

    有難うございます。
    templateについて、まだ手付かずなのですが、温かい激励と受けとめました(^^)
    ぜひ挑戦してみたいと思います。

    2013年2月11日 22:06
  • Azuleanさん

    温かいご配慮有難うございます(^^)

    2013年2月11日 22:08
  • Jittaさん

    > この様な事を抽出して、決めるべきことを決める作業を「設計」といいます。

    有難うございます。
    今回のコーディングで、この大切さが身に染みています。
    曲がりなりにもやっているつもりなものの、進めている間にだんだんぼやけてしまうため、
    コーディングを始める前だけでなく、作っている途中で適度に手を止めて、
    「やりたいことは何か」
    「何にどこでどんな役割をさせるのか」
    「これでやりたいことは出来るのか」
    と考えるタイミングをとるようにしたいと思います。

    2013年2月11日 22:08
  • 佐祐理さん

    > HAIRETSUはメンバー変数として宣言することになります。

    有難うございます。
    シンプルで分かりやすく、今回この方法にさせて頂きました。

    また、せっかくあるコンストラクターとデストラクターを有効に使うようにしたいと思います。

    2013年2月11日 22:12