はじめまして, enokoboと申します。
早速ですが, C++の文字列の寿命について質問させて頂きます。
例えば, 次のような関数があった場合,
char *GetString()
{
return Test;
}
std::cout << GetString();
などの呼び出し側で文字列を出力できてしまうのですが,
TestはGetString()関数を出るときに破棄されないのでしょうか?
何かご存知の方がいらっしゃいましたら, ご教授願います。
文が終了するまでは、一時オブジェクトが作成されているから。
関数呼び出し元で、文が終わった時に一時オブジェクトは無効になる。
RAPTさん, ご回答ありがとうございます。
すみません, まだちょっと理解できないのですが,
① 文字列の場合,
#include <iostream>
void ShowString(char *pString)
{
std::cout << pString;
}
char *GetString()
{
return test;
}
と定義されていたときに
ShowString(GetString());
という呼び出しは有効になります。
しかし,
② クラス オブジェクトの場合,
#include <iostream>
class CMyClass
{
public:
int m_iValue;
CMyClass(int iValue)
{
m_iValue = iValue;
}
};
void ShowMyClass(CMyClass *pMyClass)
{
std::cout << pMyClass->m_iValue;
}
CMyClass *GetMyClass()
{
return &CMyClass(10);
}
と定義されていたときに
ShowMyClass(GetMyClass());
という呼び出しは無効になってしまいます。
文字列のときと何が違うのでしょうか?
初歩的な質問で申し訳ありませんが, よろしくお願い致します。
> return test;
こちらは、「文字列リラテルのアドレス」を返しています。
文字列リラテルなので、メモリ上に固定的に割り当てられた領域に確保されています。
> return &CMyClass(10);
こちらは、一時的に作られたクラスのアドレスを返している…かと。
(C++勉強中故ちょっと自信なし)
こちらはたいていスタック上に作成されるので関数から戻った際に無効になります。
デストラクタが片づけている…かな?
一時オブジェクトが作成されてソレが返される場合もあるようですが。
>char *GetString()
>{
> return test;
>}
char *GetString()
{
char* c = test ;
return c;
}
どうなると思いますか?
>char *GetString()
>{
> return Test;
>}
>
>std::cout << GetString();
>などの呼び出し側で文字列を出力できてしまうのですが,
これはそう出来る実装が多いだけで、保証はされていないとお考え下さい。
この例では一時オブジェクトも作られません。
ということで、クラスオブジェクトの場合でも同様に保証はされません。
瀬戸っぷさん, ...さん(お名前でよろしいのでしょうか?), ありがとうございます。
> こちらは、「文字列リラテルのアドレス」を返しています。
> 文字列リラテルなので、メモリ上に固定的に割り当てられた領域に確保されていま
す。
なるほど, 文字列リテラルは静的なデータ領域に確保されるんですね。
だから, GetString()関数を出た後でも残っているんですね。
> char *GetString()
> {
> char* c = test ;
> return c;
> }
> どうなると思いますか?
これも, 瀬戸っぷさんがおっしゃっているように,
静的なデータ領域のアドレスですから, 問題ありませんね。
# ちょっと自身がなかったので試してみました。
②のクラス オブジェクトの場合ですが,
呼び出し元を
std::cout << GetMyClass()->m_iValue;
としたときには正確な読み出しができました。
ローカル変数の寿命は関数内だとすると,
辻褄が合っていないと思います。
スタックが破棄されるタイミングなどは,
コンパイラによってまちまちなのでしょうか?
それとも, このようなローカル変数のアクセスは正当なのでしょうか?
> (1) 文字列の場合,
> char *GetString()
> {
> return test;
> }
char *でなくconst char *とした場合,これは,
const char *GetString()
{
static const char [] __test = test;
return __test;
}
と同等です。
#文字列リテラルからchar *への変換は特殊例なので脇にのけておきます。
> (2) クラス オブジェクトの場合,
> CMyClass *GetMyClass()
> {
> return &CMyClass(10);
> }
これは,
CMyClass *GetMyClass()
{
{ // テンポラリオブジェクトのスコープ
CMyClass __temp(10);
return &__temp;
}
}
と同等です。
前者はstaticオブジェクトへのポインタを返しているのに対して,
後者はテンポラリオブジェクトへのポインタを返しています。
テンポラリオブジェクトは,その式が終了した時点で破棄されますから,
テンポラリオブジェクトへのポインタは無効なポインタになります。
> >char *GetString()
> >{
> > return Test;
> >}
> >std::cout << GetString();
> >などの呼び出し側で文字列を出力できてしまうのですが,
> これはそう出来る実装が多いだけで、保証はされていないとお考え下さい。
こっちは保障されているはずですが。
何が保障されていないかが気になります。
書いている間に返信があったので追加しておきます。
> (2)のクラス オブジェクトの場合ですが,
> 呼び出し元を
> std::cout << GetMyClass()->m_iValue;
> としたときには正確な読み出しができました。
これは「たまたま」です。
CMyClassにデストラクタを書いて,タイミングを調べてみると良いでしょう。
> ローカル変数の寿命は関数内だとすると,
> 辻褄が合っていないと思います。
> スタックが破棄されるタイミングなどは,
> コンパイラによってまちまちなのでしょうか?
テンポラリオブジェクトがどのタイミングで破棄されるかは,完全に決まっています。
次の二種類を除き,テンポラリオブジェクトが生成された完全式の評価が終了した時点で
破棄されます。
・テンポラリオブジェクトが初期化に使われている場合
・constリファレンスに束縛されている場合
> それとも, このようなローカル変数のアクセスは正当なのでしょうか?
不正です。
動いたのは,先にも書いたとおり「たまたま」です。
REEさん, すみません。書き込み中でした。
> これはそう出来る実装が多いだけで、保証はされていないとお考え下さい。
> この例では一時オブジェクトも作られません。
> ということで、クラスオブジェクトの場合でも同様に保証はされません。
つまり,
・文字列リテラルを静的なデータ領域に保持する
・スタックを破棄するタイミング
これらは, コンパイラ次第ということですか?
> >char *GetString()
> >{
> > return Test;
> >}
> >std::cout << GetString();
> >などの呼び出し側で文字列を出力できてしまうのですが,
> これはそう出来る実装が多いだけで、保証はされていないとお考え下さい。
これについては、撤回します。
よく考えたら、保証されていないと困りますね。
余計な混乱を招いて申し訳ありませんでした。
YuOさん, またそれ以前にご回答頂いた皆様, ありがとうございました。
お蔭様でようやく理解することができました。
どうも私の中で,
・文字列リテラルの寿命
・テンポラリ オブジェクトの寿命
・ローカル オブジェクトの寿命
を混同していました。
今後ともよろしくお願い致します。