//struct.cpp
struct EFONT
{
EFONT()
{
strName1=_T(MS ゴシック);
bBold1 =FALSE;
bItal1 =FALSE;
iSize1 =25;
}
CString strName1; // フォント
BOOL bBold1; // 太字
BOOL bItal1; // 斜体
INT iSize1; // サイズ
};
#define EFONTS CArray <EFONT,EFONT&>
EFONTS Fonts;
//func.cpp
EFONT* GetFontInfo(int number)
{
return &Fonts->GetAt(number);
}
//draw.cpp
LOGFONT lf;
memset(&lf,NULL,sizeof(LOGFONT)); //初期化する
lf.lfCharSet=SHIFTJIS_CHARSET; //日本語に設定する
EFONT* pFont;
pFont=GetFontInfo(0);
CString str; //表示する文字列
str=_T(ポーチ);
strcpy(lf.lfFaceName,pFont->strName1); //フォント
GetFontInfo関数にて正常にフォント情報を取得した後、
str=_T(ポーチ);←この処理後、pFontのフォント情報が書き換わってしまっています。
なぜこのような現象が起きるのか理解できていません。
どなたかご教授おねがいいたします。
と言うよりもGetFontInfo(int number)の実装に問題があると思います。
CArray::GetAtの実装は一時オブジェクトを返す実装だと思いますから
一時オブジェクトのポインタを返しては駄目だと思います。
一時オブジェクトは文字通り一時的な存在ですからCArray::GetAt呼出し後
いつまでも存在してるわけではないはずです。
CArrayをインスタンスの配列にするのであれば、受け取りもインスタンスで
行うようにする必要があると思います。別にCStringが悪いわけでは無いと思いますよ。
>CArrayをインスタンスの配列にするのであれば、受け取りもインスタンスで
>行うようにする必要があると思います。別にCStringが悪いわけでは無いと思います
よ。
つまり、こういうように実装するべきなのでしょうか?
すいません、もう少し詳しく教えていただけませんか?
EFONT* GetFontInfo(int number)
{
EFONT* pFont;
pFont=&Fonts->GetAt(number);
return pFont;
}
> つまり、こういうように実装するべきなのでしょうか?
> すいません、もう少し詳しく教えていただけませんか?
> EFONT* GetFontInfo(int number)
> {
> EFONT* pFont;
> pFont=&Fonts->GetAt(number);
> return pFont;
> }
えーと、これですとやっぱり駄目だと思います。
CArray::GetAtのMSDNの説明は確認されたでしょうか?
template< class TYPE, class ARG_TYPE >
TYPE GetAt( int nIndex ) const;
となっていますからGetAtで帰ってくるインスタンスは
インスタンスで受けないと駄目でしょう。
EFONT eft;
eft = Fonts->GetAt(number);
これなら一時オブジェクトの中身はローカル変数eftにコピーされますから
内容を保持し続ける事が出来ます。
但し、ローカルのeftのポインタを返してもその関数から抜ければ、
ローカル変数は開放されますからやはり駄目です。
この辺の話はEffective C++と言う本に解説してあるので
そういった本でじっくり勉強される事をお勧めします。
ちなみに
> EFONT* GetFontInfo(int number)
> {
> EFONT* pFont;
> pFont=&Fonts->GetAt(number);
> return pFont;
> }
これですと、pFontが指しているのはやはり一時オブジェクトですから
問題の解決になっていません。
ポインタが指す先が一時オブジェクトだから問題あるわけです。
多分、インスタンスのCArrayを使っている限り、ポインタで返すのは無理だと思います。
まあ、よっぽど面倒な実装をすれば話は別でしょうけれど、
この為にわざわざ構造を複雑にするのは本末転倒でしょうし。
確かにEFONT を返り値とするように変更すれば正常に動作します。
継承元のクラスでGetFontInfoを実装しているのですが、
以下のようなコーディングはどうでしょう?
virtual EFONT GetFontInfo(int number){ return NULL; }
EFONTを返す関数でreturn NULLというのは許されるのでしょうか?
現在、コンパイル中なので確認にはまだ時間がかかりそうなので
こんな質問をしていまいました。
やはり無理でした…
PATIO さん、本当にありがとうございました。
CArray::GetAtは一時オブジェクトを返す実装だということ
とても勉強になりました。
>CArray::GetAtのMSDNの説明は確認されたでしょうか?
>template< class TYPE, class ARG_TYPE >
>TYPE GetAt( int nIndex ) const;
VC6ではPATIO さんのおっしゃるとおりですが、
VC7.1(VC2003), VC8.0(VC2005)では、参照を返すようです。
http://msdn2.microsoft.com/ja-jp/library/dh1a0227(VS.80).aspx
VC6では GetAt() じゃなくて ElementAt() を使えば良いかと
ども、PATIOです。
> VC7.1(VC2003), VC8.0(VC2005)では、参照を返すようです。
のわっ、変わってますね。
にしても実際には参照で返された内容を値受けした方が安全でしょうねぇ。
処理コストはかさみますけれど。
> VC6では GetAt() じゃなくて ElementAt() を使えば良いかと
MSDNの説明では、
「配列内の要素への一時的な参照を返します。これは、配列への左辺代入演算子をインプ
リメントするために使います。」
と有りますので一時的というのが気にならなくはないですね。
実装がどうなっているのか見てみないとわかりませんけれど。
配列に対する追加削除を行った時に内部的に移動してしまう可能性も
ありますから参照を保持するのはあまり得策ではないかもしれません。
CArrayクラスが内部的に移動可能なメモリで確保されていたら
その参照がいつまでも使えるかどうかわかりませんし。
日本語がおかしかったのでちょっと補足。
> CArrayクラスが内部的に移動可能なメモリで確保されていたら
> その参照がいつまでも使えるかどうかわかりませんし。
CArrayクラスがデータ保持用のメモリを内部的に移動可能なメモリで確保していたら
というのが言いたかった事です。
移動可能なメモリ場合、OSの都合で移動される可能性があるので必要な時にその都度
参照するようにしないと参照先がなくなっている可能性も否定はできないかなと。
>> VC6では GetAt() じゃなくて ElementAt() を使えば良いかと
> 配列に対する追加削除を行った時に内部的に移動してしまう可能性も
> ありますから参照を保持するのはあまり得策ではないかもしれません。
CArray(VC6)の実装を見てみたところ、参照を保持するのは得策というかダメですね。
指摘ありがとうございます。
http://msdn2.microsoft.com/ja-jp/library/dh1a0227(VS.80).aspx
には「一時的な参照」という記述が無いようだけど内部実装を変えたのだろうか?
それとも「参照を返してやるけど解った上で使えよ。お前ら」って感じなんだろうか。
今度ソースを手に入れて見ておこ。