VCで扱うグローバル変数に関して

[戻る]
JON 2010/08/31(火) 11:31:24
複数のソースで一つの文字変数を共用したいと考えています。

今ヘッダファイルに
Test1.h

external Cstring Global;

を宣言しています。


------Test1.cpp-------------
#include "Test1.h" 

CString Global;
Global = "Hoge";

------C2.cpp-------------
#include "Test1.h" 

CString Global;
CString str;
str = Global;


とした場合、C2.cppのStrの値は""です。
Test1.cppの時点ではGlobalに値が入っているのですが…。
やり方間違っていますでしょうか?



違う話にはなってしまいますが、そもそもこのやり方をとっているのは、コンボボックス
の値がうまくとれないからです。
Test1.hのクラス(ClassTest)内で
CComboBox     m_Err;
としてあり、

■Test1.cppでやった場合
m_Err.GetWindowText( str);
str変数に現在選択されているコンボボックスの値がとれるのですが、

■C2.cppでは
ClassTest CTest;
CTest.m_Err.GetWindowText( str );
でstrに値が入ると思ったらできません。

なので、グローバル変数を使用してTest1.cppでゲットした文字列をC2.cppで参照できる
ように考えたのが経緯です。

うまく説明できたかわかりませんが、色々調べてもなかなかC2.cppで参照できない(また
は、コンボボックスの値が取得できない)ので、
このようなときはどうするのがいいのか教えてください。
よろしくお願いします。

tetrapod 2010/08/31(火) 11:52:17
グローバル変数に対する技術的解説はしてあげることができるけど、
今困っている内容をグローバル変数を経由して解決させるのは愚の骨頂なので
そんな解説をする必要はない、と俺は判断する。

「正しいやり方」で解決するべきだと思うぞ。

で・・・先は長そうなんだけど、「インスタンス」ってわかる?わかってたら
> ■C2.cppでは
> ClassTest CTest;
> CTest.m_Err.GetWindowText( str );
なんてコードは書かないと思うんだけど。

JON 2010/08/31(火) 13:31:10
すいません、インスタンスに関してなんとなくしか把握していません。

そのなんとなくのままやってみるのもまずいかもですが、
とりあえず下記のようにやってみました。

■C2.cpp
ClassTest *CTest = new ClassTest;
CTest->m_Err.GetWindowText( str );

これもstrの値は""だったので、


■上記とは別方法
Test1.cppで
m_Err.GetWindowText(Name); //NameはClassTest内のCString

C2.cppで
ClassTest *CTest = new ClassTest;
str = CTest->Name;

としてもstrは""でした。

自分でも勉強しつつやっているので、もっと理解をふかめてから質問するべきなのでしょ
うが、行きづまってしまっているのでよろしくお願いします。

tetrapod 2010/08/31(火) 14:02:11
---a.cpp---
int i=1; // ある場所で i に1をセットした

---b.cpp---
int i; // 先の a.cpp 中の i を欲しいと思った
printf("%d\n", i);

としても a.cpp 中の i の値は得られない。んだけど、これが
・得られないのは当たり前と思うか
・なぜ得られないのか疑問に思うか
どっちだろう?

あるいは int* p=new int; を2箇所に書いたとして、
それらは別々の変数を作っているわけで、お互いの値が連動するわけが無い。

今悩んでいるのは上記の int が ClassTest に置き換わっただけの話。

で、どう直すか・・は、どう直すべきなのか *求められている仕様次第* で違う。

maru 2010/08/31(火) 14:20:17
この辺のWindowsオブジェクトの操作はC++知識だけでなくMFCの知識を必要とするから
初心者にはわかりにくいだろうね。

> ClassTest *CTest = new ClassTest;
> CTest->m_Err.GetWindowText( str );
この*CTestというインスタンス(クラスオブジェクト)は実際のダイアログボックスと
関連していないから、そのメンバ変数であるm_Errも実際のコンボボックスと対応がと
れていない。従ってGetWindowTextを使用することはできない。
test1.cppでコンボボックスの文字列が取得できたのは、クラスウィザードによって生
成されたコードでm_Errに実際のコンボボックスクラスを割り当てているから。

目的である、「別のソースでコンボボックスの文字列を取り出す」ということをやり
たいのなら、このダイアログボックスを誰(どのクラス)が所有しているのか?とい
うところから考える必要がある。その上で、必要とするクラスにこのクラスからダイ
アログボックスクラスオブジェクトのアドレスを教えてあげるようにする。

> で・・・先は長そうなんだけど
ですね。

> tetrapod さん
> ---a.cpp---
> int i=1; // ある場所で i に1をセットした
> ---b.cpp---
> int i; // 先の a.cpp 中の i を欲しいと思った
> printf("%d\n", i);
多分これを実現しようとして
> Test1.h
> external Cstring Global;
としているんだと思いますよ。
問題に対する解決方法として正しくないというご指摘には同意しますが。

JON 2010/08/31(火) 15:02:58
すばやい回答ありがとうございます。

> 今悩んでいるのは上記の int が ClassTest に置き換わっただけの話。
確かにです。

> C2.cppで
> ClassTest *CTest = new ClassTest;
> str = CTest->Name;
としても意味がありませんでした。
新しいものを作ってそれを参照しているのだから""ですよね。

ご指摘があるようにグローバル変数で逃げるようなことはせず、ちゃんと理解して、解決
していきたいと考えています…。


> 目的である、「別のソースでコンボボックスの文字列を取り出す」ということをやり
> たいのなら、このダイアログボックスを誰(どのクラス)が所有しているのか?とい
> うところから考える必要がある
目的はそうです。
たしかに所有していないものを参照しても上記でいった意味のない現象と同じですね。


> クラスでtest1.cppでコンボボックスの文字列が取得できたのは、クラスウィザードに
> よって生成されたコード
コンボボックスの値を現在保持しているものはTest1.hの中にCComboBox  m_Err;
があるのでそうだと思っているのですが、これを生成した部分?から取り出す、というこ
とですか?

よろしくお願いします。

tetrapod 2010/08/31(火) 16:00:15
この辺は「責任分担」(設計方針) という話になってくる。

どうやって ClassTest hoge; 変数にアクセスするの?
そもそもアクセスしていいの?(それが許されているの?)
ComboBox である hoge.m_Err を C2.cpp 中から取り出してよいかどうか?
読み出してよいのは ComboBox 自体ではなくてその中の Text ではないのか?

などなど、「どう実装すべきか」は「どう設計したか(今後するか)」で違う。

class ClassTest と C2.cpp の関係はどうなっているのか?
などなど、今まで書かれた発言中ではわからないことが重要。

例えば『俺アプリ COreOreApp 』を作っていて、
「何らかの設定を行うダイアログ COreSettingDialog」が必要になったとして
その実装は以下のような感じになると思われる。
void COreOreApp::OnSetting() {
 COreSettingDialog d;
 if (d.DoModal()==IDOK) {
  // ここで ComboBox m_Err; 中のテキストを読んで使いたい
  // DoModal 完了時点で ComboBox は消滅しているので、直接は無理
  // COreSettingDialog は必要な値を CString TextErr に返すよう作る
  TRACE(d.TextErr);
 }
}
とかなんとか。

無関係なもの同士でデータをやり取りするってのは設計がなってない証拠だし
関係があるもの同士なら、適切なやり取りの仕方ってのはわかるはず。

JON 2010/08/31(火) 17:02:00
回答ありがとうございます。

私のほうがどういった経緯でそのような設計にしているか情報伝達不足でしたね。
再度確認とらせてください。
現状あまり変わっていませんが、下記のようにしました。

-----Test1.h-----
class ClassTest{
  ・
  ・
 C2Class C2Cla;
}

-----C2.h--------
class C2Class{
  ・
 ・
 CString Name;
}

-----Test1.cpp--------

ClassTest::StartButton
{
 m_Err.GetWindowText(C2Cla.Name); 
}

-----C2.cpp--------
void Shori
{
  //この中でNameを使いたい
}


ここで疑問点があります。
・Shori関数はC2Class::ShoriであればNameをそのままひっぱることができますが、
 C2.cppで宣言された関数であること。
 なので、Nameをどうしたらいいか悩み中です。

・C2ClaをClassTest内で使用しなければ、できそうですが、結局グローバル変数になっ 
 てしまう。Shoriの中で、ClassTestが持っている共通な変数の値を参照するのはどう 
 したらいいのでしょう?

※上記疑問点でおかしなこと言っていたらすいません。また先ほどの疑問から進展してい
 ないような疑問ですいません

どうしても疑問を解消するにはグローバル変数の考え方になってしまいます。

tetrapodさんの言われているように、設計の仕方でどのように参照し、値を使うかはしっ
かり考えなければいけませんね。
今回はその段階にいくまえに疑問を解消したいところです…。

なかなかつかめていなくてすいません、よろしくお願いします。


  

maru 2010/08/31(火) 18:34:48
> コンボボックスの値を現在保持しているものはTest1.hの中にCComboBox  m_Err;
> があるのでそうだと思っているのですが、これを生成した部分?から取り出す、とい
うこ
> とですか?
違います。
CComboBoxクラスは実際にコンボボックスの値を保持していません。ウィンドウに
表示されているコンボボックスとのインタフェースを実現するものです。

> ・Shori関数はC2Class::ShoriであればNameをそのままひっぱることができますが、
>  C2.cppで宣言された関数であること。
>  なので、Nameをどうしたらいいか悩み中です。
これはShori関数がグローバル関数であるとの認識でよいでしょうか?
だったらNameを引数にして渡せばよいでしょう。ただし、そのためには呼び出し元で
ClassTestクラスのインスタンスがわかっている必要があります。

> ・C2ClaをClassTest内で使用しなければ、できそうですが、結局グローバル変数にな
っ 
>  てしまう。Shoriの中で、ClassTestが持っている共通な変数の値を参照するのはど
う 
>  したらいいのでしょう?
全く何を言っているのか理解できません。
「ClassTestが持っている共通な変数」とは何を指すのでしょうか?共通って何と何?

普通は値を受け渡しすればいいことですよね。クラスのメンバ変数を受け渡しするた
めにメンバ関数を作成してそのメンバ関数を呼び出して値の授受を行う。メンバ関数
を呼び出すためにはそれぞれのクラスオブジェクトを呼び出し元で参照できるように
しておく必要がある。それぞれの関係がわかっていれば、tetrapodさんの言っている
通り
> 無関係なもの同士でデータをやり取りするってのは設計がなってない証拠だし
> 関係があるもの同士なら、適切なやり取りの仕方ってのはわかるはず。
と思います。

> 私のほうがどういった経緯でそのような設計にしているか情報伝達不足でしたね。
ClassTestクラス、C2Classクラス、Shori関数およびその呼び出し元の関係がわから
ないと適切な方法をアドバイスするのは困難。

> 今回はその段階にいくまえに疑問を解消したいところです…。
その疑問自身に意味があるのか疑問。
まず、それぞれのクラス、関数の役割、関係を整理すべき。

maru 2010/08/31(火) 18:59:12
> void Shori
> {
>    //この中でNameを使いたい
> }
がグローバル関数だとしたら、普通は
-----C2.h--------
void Shori(const CString& Name);
-----C2.cpp--------
void Shori(const CString& Name)
{
 // Nameを使用
}
-----Test1.cpp--------
#include "C2.h"
ClassTest::StartButton
{
  CString Name;
  m_Err.GetWindowText(Name);
  Shori(Name);
}
となるんじゃないの。

Shori関数をClassTest::StartButtonの中からは呼び出したくない
のであれば、
-----Test1.h-----
class ClassTest{
  ・
  ・
 CString m_Name;
  void StartButton() {   m_Err.GetWindowText(Name); }
  CString GetName() { return m_Name;}
}
-----ダイアログの呼び出し元-----
ClassTest dlg;
if (d.DoModal()==IDOK) {
  CString Name = dlg.GetName();
  Shori(Name);  
}
ダイアログボックスを使った、ごく一般的な処理方式そのものです。

tetrapod 2010/08/31(火) 23:25:02
CWnd がどうのこうの ComboBox がどうのこうの、の前に
> 呼び出し元でClassTestクラスのインスタンスがわかっている必要があります。
がばっちり理解できているのかどうかが不安。

instance という単語は事例と訳されるが、この業界では [実体] とでもしておくか。
いろんな instantiation インスタンス化があるわけだけど、そのひとつには
「変数を作る」ことがあげられる。

// 提示例の C2Class や ClassTest と同じと考えてくれ
struct hoge { CString foo; ... };
struct piyo { hoge bar; ... };
とだけあるとき、変数 foo や変数 bar は無いんだ。インスタンス化はされていない。
このことを100%理解できているかい?

void func1() {
 piyo aaa; // としたとき初めてインスタンス化が起きる
// この行では aaa.bar や aaa.bar.foo といった変数が存在し、使用できる
 ...
} // 対応する閉じ括弧に達したら aaa は消滅し、使えなくなる
// そのとき aaa.bar や aaa.bar.foo も運命を共にする(同時に使えなくなる)

void func2() {
 Shori(); // ここで先の aaa や aaa.bar.foo を使おうとしても、そのすべは無い
}
なぜなら func1 と func2 は無関係の赤の他人だから。

最初の発言で書かれたコードがやろうとしているのは
void func3() {
 piyo aaa; // これは func1 の aaa とは無関係なので値が入っているわけがない
 処理(aaa.bar.foo); // しても無駄
}
ということだ。

以下ならば [インスタンスがわかっている] わけで正しいプログラムとなる。
void func4() {
 piyo aaa; // ここで作った変数 aaa に対して
 Shori(aaa.bar.foo); // その値の一部を使おうとしている
}

この辺の話、きっちり理解できなきゃ先に進めないと思うぞ。

JON 2010/09/01(水) 09:22:17
回答ありがとうございます

maruさん
ShoriはC2.cpp内で宣言されている関数なので、C2.hで宣言しているものではありませ
ん。
なので、現状はTest1.cppからは呼べないです。
また、Shoriに引数を持たせて最終的に渡すのも現状難しいです。
関数をいくつかへだててShoriを行っている状況です。


tetrapodさん
> とだけあるとき、変数 foo や変数 bar は無いんだ。インスタンス化はされていない。
> このことを100%理解できているかい?
内部の処理等100%理解はできていませんが、tetrapodさんの言われている事に関しては
理解しているつもりです。


現在は
C2ClassでSetName(CString ErrName);の関数を作成して、

Test1.cppで
m_Err.GetWindowText(Name);
C2Cla.SetName(Name);

としてC2.cppで使えるように渡しています。

しかしこれもSetName関数内でC2.Cpp内で宣言した変数に置き換えて使用しています。
いちおこれでうまく受け渡しはできていますが、問題の解決にはなっていない気がしま
す。

PATIO 2010/09/01(水) 10:09:28
あるクラスAが持っている情報を他のクラスに渡して使いたい場合、
クラスAの実体(インスタンス)が何処で生成されていて
何時から何時まで存在しているのかと言うことが問題になります。
クラスAの実体からデータを取り出せるのは、その実体を参照できる
所でしか出来ません。従ってモーダルダイアログの実体を
メンバー関数内のローカルで取っていた場合はそのメンバー関数の
中でのみ実体が存在する事になりますから、メンバー関数の中で
必要な値は取り出しておく必要があります。
取り出した情報はそのクラスで保持しておいて
使いたい他のクラスの実体を参照できるタイミングで
引き渡す事になります。

結局の話、メインウインドウに相当するクラスの中で必要なデータを
管理するべきで、ダイアログ等で扱う内容でアプリケーション全体で
扱う必要がある情報はメインウインドウに相当するクラスで管理すべき
と言う話になります。
ダイアログは入出力のインターフェイスくらいに考えた方がすっきりしますね。

メインウインドウに相当するクラスに関してはSDIとかMDIとか
ダイアログベースとかそういう種類で対象になるクラスが若干変わるので
ここでは書きませんが、それぞれの構成で各クラスがどういう役割を
持っているのかをきちんと把握する必要があります。

MFCのフレームワークを理解する事が、プログラミングをうまく行なう為に
必要な条件になりますのでその辺は粘り強く勉強される事をお勧めします。

JON 2010/09/01(水) 10:32:48
[[解決]]
そうですね、勉強になりました。
自分でも色々調べて、新たにわかったことなど出てきました。

まだ、MFCがはっきり理解できていませんのでまずそこを理解してからですね。
また何かわからないことありましたら、アドバイスください。

ありがとうございました。

maru 2010/09/01(水) 11:10:43
> ShoriはC2.cpp内で宣言されている関数なので、C2.hで宣言しているものではありませ
> ん。
C2.cpp/hはいじれないってこと?
だったら、C2.cpp/hとC2Classは無関係ってことかな?
そのソースファイルとクラスの関係を説明できる?

どのソースファイルから関数が呼べる/呼べないって話をしているけど、重要なのは
どのクラスから呼び出すかってことで、どのソースファイルかってことは重要では
ない。その辺わかってる?

> しかしこれもSetName関数内でC2.Cpp内で宣言した変数に置き換えて使用しています。
この記述からすると、やはりグローバル変数を経由しているってことかな?

> また、Shoriに引数を持たせて最終的に渡すのも現状難しいです。
> 関数をいくつかへだててShoriを行っている状況です。
必要な引数はその間の関数に渡していくのが基本。
# それがいっぱい有って大変だからやりたくないって気持ちもわからないではないけ
ど。

そもそも、関係が有るはずの関数間にいくつものクッションが有るって設計がおかし
い。
それをグローバル変数を使って解決したとしても後で苦労するだけだよ。
設計から見直したほうがよさそう。

古いスレッドには追加発言できません。