別のクラスの関数、変数のアクセス – 固定ページ 2 – プログラミング – Home

別のクラスの関数、変数のアクセス
 
通知
すべてクリア

[解決済] 別のクラスの関数、変数のアクセス

固定ページ 2 / 2

asap
 asap
(@asap)
ゲスト
結合: 20年前
投稿: 13
Topic starter  

PATIO さん

>思うにC言語とかC++言語の変数のスコープと確保される場所に関する記述を
>読み直されたほうがよいと思います。
>もし入門書の類を通読した事がないということであれば、
>言語の入門書を購入して演習問題も含めて通読される事をお勧めします。
>こういった基礎知識はとても大事なのにどうしても軽視されがちです。

すみません。痛いところをつかれました。 通読はしてませんでした。
その時々で必要なところを探して調べて参考にしていただけでした。
スコープと書かれて???と正直思いました。 用語と動作が一致
していないことが多くなかなか意味がわかりませんでした。
スコープは変数の適用範囲のことですね?
auto変数は通常スタック領域というところに記憶されていて
ローカル変数などはその関数が終わると勝手に消えてしまうかもしれない?。

本などにはグローバル変数はなるべく使うな的なことがかいてあるのですが
どうしても関数間で参照する変数を多用することが多くいつもグローバル
変数をたくさん定義して多用することが多かったです。

そこで今更ながらの疑問ですが、ローカル変数はその関数が終わると
関数内で定義している変数はスコープから消えてしまうが、グローバル
で定義されている変数はずっとスコープに留まってしまう。
そうするとスコープで自由になるメモリーが少なくなってしまうから
グローバルで定義する変数はできる限り使ってはいけないということでしょうか?

>対処療法だけで直した不具合ほど怖い物はありません。
>原因究明できていない不具合は解決したとは言えません。

そうですよね。いつもつい場当たり的な事でごまかしごまかししてました。
一見うまく動いてそうにみえても実は大きな爆弾が隠れてるなんて事があるんですね。
正直ってポインタ、. ->とかクラスとか今でも悩ましいです。
ありがとうございます。
もっと勉強しなければいけえないですね。


返信引用
KING・王
 KING・王
(@KING・王)
ゲスト
結合: 20年前
投稿: 122
 

> そこで今更ながらの疑問ですが、ローカル変数はその関数が終わると
> 関数内で定義している変数はスコープから消えてしまうが、グローバル
> で定義されている変数はずっとスコープに留まってしまう。
> そうするとスコープで自由になるメモリーが少なくなってしまうから
> グローバルで定義する変数はできる限り使ってはいけないということでしょうか?

グローバル変数は、プロセス内のどこからでもアクセスできるため、
どこでその値を変更されるか分からなくなります。

特にマルチスレッドのような場合、関数内の処理の途中で、
別のスレッドが割り込み、値を変更してしまうような場合もあります。

よって、グローバル変数を使用しないことが推奨されます。
#と、私は理解しております。違うようでしたら、識者の方々、フォローをお願いします。

#マルチスレッドのプログラムを複数人で作成したときなどに、
#かってに、他の人の作成したスレッドで値を変更され困りました。
#デバッグしても、タイミングの問題で、なかなか発見できなかったりしますし。。。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

私がグローバル変数の使用を避けようとするのは、
そのモジュールの結合度が強くなって再利用しにくくなるからです。
クラスでも関数でも良いのですが、
モジュールとして再利用するのであれば、そのクラスや関数内で
全て解決されていないと再利用が難しくなります。

C++を使ってオブジェクト指向で組んでいくのであれば、
再利用するかどうかも含めて考慮の対象になりますが、
クラスの再利用を頭において設計するので基本的にグローバル変数は
使用しません。
クラスのメンバー変数にするのがほとんどです。
さらにサイズが大きくなる事が容易に予想される場合は、
単純なインスタンスとして宣言せずにコレクションクラスを使ったり、
ポインター宣言しておいて使用時にヒープ領域からアロケートします。
(この辺はある程度知識があれば、皆さんされていると思います)

どの程度を大きいと判断して処理を分けるかですけれど、
人それぞれみたいですね。私は1KBを超えるような物はローカルで
宣言する事は無いです。いまはスタックサイズのデフォルト値が
かなり大きく設定されていますけれど、昔は小さかったので数百バイト
でもローカルには置かないようにしてました。
当時に比べれば、今のメモリ事情は恵まれています。

グローバルに出す物ですけれど、
デバッグ用に一時的に使いたい物なんかはグローバルに出す事がありますね。
これは最終的にはプリプロセッサで殺しますけれど。
あと、スレッド間のやり取りを行うための共有データもグローバルに
置く事が多いです。最もこの場合は排他を入れる事が前提になります。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> スタックというのは簡単に言えばひとつの関数単位で扱える
> メモリサイズと考えてよいのでしょうか?
> ただ、その関数が他の関数を呼び出していればその分も合算して考える。

違います。関数ではありません。システムの実行単位(Windows ならスレッド)毎です。
概念はCPU や OS の仕組みについて情報処理の参考書などを確認してください。
構造がstack方式だと考えれば、累積の仕方も見当がつくのではないでしょうか。
コーディング上の留意点はちゃんとしたC の本なら書いてあると思います。

> OnUpdate内に希望する処理を書き込んだほうが解決の近道なんでしょうか?

MVC や MFC の理解度にもよりますし既存の作りにもよります。
動かすだけなら遠回りかもしれません。
ただ、おそらく構造としてより好ましいと思います。

> そうですかうまくviewの関数を呼び出せなかったのですか?

多分そうです。Rom の方がアクティブなら当然、
ActiveFrame -> ActiveView の結果も Rom の方が返ります。
それを無理やり「お前は View のはずだ」と決め付けても View のようには動けません。

> また難しい言葉が出てきてしまった。

前者は MFC、後者はC++の機能です。機能は大差ありませんのでお好みでどうぞ。
GetAvtiveView や EnumChildWindows、GetFirstViewPosition などの
結果が「本当に View かどうか」の確認することができるはずです。

> 例の配列は3つ程存在してるはずです。

(処理系にもよりますが)グローバル変数はまた扱いが違います。
溢れたのはスタックであり、それ以外の領域にいくつ配列が存在しようと関係ありません。
仕組みをよく理解せずに修正している以上、直した本人からすれば
「ちょっとした変更」のつもりでも実際にそうとは限りません。
800KB 級の配列を複数取りまわせばうっかり 1MB を超えるのも簡単です。
綱渡りでたまたま動いていたと考えて貰っても構わないと思います。

> foo[3][7] = foo[3][4] + foo[3][4] ;

こういう記述も可能です。具体的な使い方は STL の vector を調べてください。

以下、横槍ですが、

> そこで今更ながらの疑問ですが、ローカル変数はその関数が終わると
> 関数内で定義している変数はスコープから消えてしまうが、グローバル
> で定義されている変数はずっとスコープに留まってしまう。
> そうするとスコープで自由になるメモリーが少なくなってしまうから
> グローバルで定義する変数はできる限り使ってはいけないということでしょうか?

スコープとはその変数が参照可能な期間であり、その存続期間とは別ものです。
両者が同一な変数が自動変数ですが、他の種類のものでは一致しません。
また、関数内に複数のスコープを作れますし、スコープと関数も別ものです。
スコープはただの枠と考える方がいいです。そこに変数が溜まったりもしませんし、
スタックならともかく「スコープで自由になるメモリ」という概念も普通はありません。

> 対処療法だけで直した不具合ほど怖い物はありません。
> 原因究明できていない不具合は解決したとは言えません。
> OSの知識、言語の知識、開発環境の知識が必要です。

PATIO さんのおっしゃる通りだと思います。

また、特に識者でもありませんが、
グローバル変数が忌避される理由はメモリの問題ではありません。

「構造化設計」等について調べてみるといいと思いますが、
分割の仕方が不適切なことが多く、モジュール強度が落ちて結合度が上がります。
これは一般に、品質や生産性、保守性などが低い(=ようは設計が汚い)と評されます。
当然、KING・王さんのおっしゃるような問題も起こりがちです。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

蛇足になりますが、

KING・王さんが言われている問題は確かに起こりうる話なんですが、
グローバルでデータを共有できるというのは、スレッドの利点でもあると
私は考えているのでグローバルにデータを置くこと自体は問題ではなくて
排他とかグローバル領域の使い方を含めた設計とかそういう部分が足りない
事が問題なのだと思っています。

これがプロセスになるとこういう手軽な方法でデータの共有が出来ません。

私がグローバルにデータを置く場合は、初めから共有データとして
設計し、C++であれば、共有データクラスとして設計します。
メンバー変数は全てprivateで作成し、アクセスにはメンバー関数を
使用します。メンバー関数に排他の機能を組み込んで
データアクセスはこの関数からしか出来ない仕組みにします。

勿論、スレッド上で共有すべきでないデータをグローバルに置くのは
ナンセンスですし、置く事に必要性が無いのであればグローバルに置くべき
では無いと思います。
この辺は設計を行うときにきちんと詰めておくべき部分だと思います。


返信引用
asap
 asap
(@asap)
ゲスト
結合: 20年前
投稿: 13
Topic starter  

KING・王 さん

>特にマルチスレッドのような場合、関数内の処理の途中で、
>別のスレッドが割り込み、値を変更してしまうような場合もあります。
わかりました。
グローバルにすると誰かが勝手に書き換えてしまう可能性が
あるんですね。この辺も気をつけていきたいと思います。

PATIO さん

>クラスの再利用を頭において設計するので基本的にグローバル変数は
>使用しません。

確かにグローバル変数を利用すると関数のコピーペーストだけでなく
グローバル変数の宣言をしなおさないといけないので不便がしょうじると
いうことですね。

>単純なインスタンスとして宣言せずにコレクションクラスを使ったり、
>ポインター宣言しておいて使用時にヒープ領域からアロケートします。
>(この辺はある程度知識があれば、皆さんされていると思います)

この辺はまったく無関心でした。
とりあえず動けばよいという感じでしかなかったので
もっと勉強して無理のないものにしていきたいです。

>排他とかグローバル領域の使い方を含めた設計とかそういう部分が足りない
>事が問題なのだと思っています。
この辺になるとだいぶ高度な感じがします。

Ban さん

>違います。関数ではありません。システムの実行単位(Windows ならスレッド)毎です。
>概念はCPU や OS の仕組みについて情報処理の参考書などを確認してください。
>構造がstack方式だと考えれば、累積の仕方も見当がつくのではないでしょうか。
>コーディング上の留意点はちゃんとしたC の本なら書いてあると思います。

そうですか。そうしますとCPUとかOSとかちゃんと知ってないと確かに
どの変まで大丈夫だとかわからないですね。

>MVC や MFC の理解度にもよりますし既存の作りにもよります。
>動かすだけなら遠回りかもしれません。
>ただ、おそらく構造としてより好ましいと思います。
>多分そうです。Rom の方がアクティブなら当然、
>ActiveFrame -> ActiveView の結果も Rom の方が返ります。
>それを無理やり「お前は View のはずだ」と決め付けても View のようには動けませ
ん。

後述しますがちょっとはまりました。
今回の原因はウインドウの作成の仕方悪かったようです。

>溢れたのはスタックであり、それ以外の領域にいくつ配列が存在しようと関係ありませ
ん。
>仕組みをよく理解せずに修正している以上、直した本人からすれば
>「ちょっとした変更」のつもりでも実際にそうとは限りません。
すみませんこの辺はちょっと理解しにくいです。
もっと勉強します。

>以下、横槍ですが、
> そこで今更ながらの疑問ですが、ローカル変数はその関数が終わると
> 関数内で定義している変数はスコープから消えてしまうが、グローバル
> で定義されている変数はずっとスコープに留まってしまう。
> そうするとスコープで自由になるメモリーが少なくなってしまうから
> グローバルで定義する変数はできる限り使ってはいけないということでしょうか?
自分で読み直すと変ですね。スコープとスタックを間違えて書いてしまいました。

>「構造化設計」等について調べてみるといいと思いますが、
使い回しがしにくいということでしょうか?
確かにグローバルとかあるとその辺が不便ではありますね。
あと関数が自作関数などを呼び出したりするのも使い回しが
しづらくなるということでしょうか?

>これは一般に、品質や生産性、保守性などが低い(=ようは設計が汚い)と評されます。
すみません。自分の書いたソースですけど書いてる時はまだよいのですが
時間がたってから改めて見直すとすぐには理解できません(笑
解読作業が必要になってきます。
他の人が見たときコメントが多いけどわかりづらいと言われました。


返信引用
asap
 asap
(@asap)
ゲスト
結合: 20年前
投稿: 13
Topic starter  

皆様 ありがとうございます。やっと解決しました。

しましたが、やはり対象療法的に行ってしまったので原因がこれだと
はっきりはいえないです。すみません。

CDocument::UpdateAllViews, CView::OnUpdate を使用しましたが、
うまくいきませんでした。
rom側ウインドウから呼び出すとrom側のOnUpdateが呼ばれるだけで
view側のOnUpdateは呼ばれませんでした。

試しにview側からよ呼ぶと今度はview側のOnUpdateだけしか呼ばれません
でした。
要は呼んだ側のOnUpdate だけ呼ばれる状態でした。

ここまできてウインドウの作成に問題があるのかと思い色々調べた所
どうやらウインドウの作成に問題があったようでした。

変更した箇所

Appクラスの InitInstance()

//変更1
//ROMウインドウ追加 変更前
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_RESTSITYPE,
RUNTIME_CLASS(CRestSimulationDoc),
RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム
RUNTIME_CLASS(CRestSimulationRom));
AddDocTemplate(pDocTemplate);
     
↓ pDocTemplateをromウインドウ用に新たに宣言
↓ リソースのstring tableでIDR_RESTSITYPE2を作成
     ↓ IDR_RESTSITYPEの内容から
     ↓  \nRestSi\nRestSi\n\n\nRestSimulation.Document\nRestSi Document
  ↓ ないの RestSiを削除したものを利用

//ROMウインドウ追加 変更後
CMultiDocTemplate* pDocTemplate2;    ←pDocTemplate2に変更
pDocTemplate2 = new CMultiDocTemplate( ←pDocTemplate2に変更
IDR_RESTSITYPE2,             ←IDR_RESTSITYPE2に変更
RUNTIME_CLASS(CRestSimulationDoc),
RUNTIME_CLASS(CChildFrame), // カスタム MDI 子フレーム
RUNTIME_CLASS(CRestSimulationRom));
AddDocTemplate(pDocTemplate2);    ←pDocTemplate2に変更

//変更2
// コマンドラインでディスパッチ コマンドを指定します。
if (!ProcessShellCommand(cmdInfo))←コメント扱いから有効に戻し
return FALSE;

//変更3
↓ここから削除
POSITION pp = GetFirstDocTemplatePosition(); //起動時に一番目の
//ウインドウを表示
GetNextDocTemplate(pp)->OpenDocumentFile(NULL);
POSITION p = GetFirstDocTemplatePosition(); //起動時に二番目の
//ウインドウを表示
GetNextDocTemplate(p);
GetNextDocTemplate(p)->OpenDocumentFile(NULL);
ここまで削除

↓ 初期のウインドウ作成を下記の方法に変更

pMainFrame->OpenView(pDocTemplate2);//mainフレーム内の自作関数

すみませんこれの中身は下記のホームページを参考にしました。
そのまま転載するのもよくないのでリンクを記述します。
直リンクが許可されていないようなのでトップページから記載します。

http://www.alpha-net.ne.jp/users2/uk413/index.html
→Atelier U-kiのトップページ→プログラマーのお部屋
→C++編 VC++小手先のテクニック集
→1つのドキュメントを異なる2つのビューで表示する

なんとなくこの変更3の改良で直ったようなのですが変更前と
どう違うのか自分では理解できませんでした。

変数のほうはこんな風に本で調べて宣言すればよいのかと思ったのですが

int (*line_5)[30] = new int[20000][30];

ローカル変数ですとコンパイルできるのですがヘッダーにグローバルで
宣言しようとするとコンパイルできませんでした。
また、調べて見ます。

みなさま色々とアドバイスありがとうございました。
おかげさまで解決できました。まだ、完成までは時間がかかりますが
色々とがんばっていきます。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

上記は MFC の Document-View を理解していけば分かってくると思います。

解決になってますが、気になった点だけ少し。

> 確かにグローバル変数を利用すると関数のコピーペーストだけでなく
> グローバル変数の宣言をしなおさないといけないので不便がしょうじると
> いうことですね。

PATIOさんのおっしゃる再利用性はモジュール単位の話です。
コピー&ペーストは昨今推奨されてませんし論点が違うと思います。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

> PATIOさんのおっしゃる再利用性はモジュール単位の話です。
> コピー&ペーストは昨今推奨されてませんし論点が違うと思います。

そうですね。コピー&ペーストでは無くてクラスを丸ごと再利用する事を言っています。
ソースレベルで再利用するのか、ライブラリ化して再利用するのかはわかれると思います
けれど。
少なくともそのクラスに関連したヘッダーファイルとソースファイルを丸ごと持って
いってそのまま使えると言うのが私の言っている最利用です。
いくつかのクラスが対を成して機能を構成しているならいっそのことライブラリ化して
使った方がいいと思います。

そういう使い方をする場合、グローバルで受け渡しをしているとどうしても
使う側の変更が必要になります。
しかもグローバル変数の名前がぶつかったりしていると使う側で合わせたりしないと
いけなくなるので再利用性が落ちますよね。

そういう意味でグローバル変数の多用は害があると言う話です。
但し、グローバル変数にする必然があるのであれば、それまで否定する必要はないと
思います。グローバル変数にする事で全体の構造がわかりやすくなるのであれば、
使うこと自体には問題はありません。
それに絡むクラスを再使用可能にしたいかといった部分も影響するでしょう。
後は、どう設計し、どう実装するかです。


返信引用
asap
 asap
(@asap)
ゲスト
結合: 20年前
投稿: 13
Topic starter  

Ban さん

>上記は MFC の Document-View を理解していけば分かってくると思います。
見た目に変わったようにみえなくても内部的には変わってるんですね。
今まではダイアログベースでしか作成してませんでした。
ウインドウとかフレームとかチンプンカンプンなのでもうちょっと勉強します。

>コピー&ペーストは昨今推奨されてませんし論点が違うと思います。
コピー&ペーストはよく使います。
ここでいう再利用というのはもっと大きなものというか構成ということですね。

PATIO さん

>しかもグローバル変数の名前がぶつかったりしていると使う側で合わせたりしないと
>いけなくなるので再利用性が落ちますよね。

変数名は自分もよく悩みます。

>但し、グローバル変数にする必然があるのであれば、それまで否定する必要はないと
>思います。グローバル変数にする事で全体の構造がわかりやすくなるのであれば、
>使うこと自体には問題はありません。
了解しました。

>それに絡むクラスを再使用可能にしたいかといった部分も影響するでしょう。
>後は、どう設計し、どう実装するかです。
自分の場合せいぜい関数単位での再利用は行ってますが、モジュールとかでは
できてないです。


返信引用
固定ページ 2 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました