• ベストアンサー

MFCのCStringについて

MFCで、CStringをメンバとして含むクラスと、このクラスのオブジェクトを動的に生成して値を代入し、生成したオブジェクトへのポインタを返す関数を以下のように定義しました。 class Record { public:  long Id;  CString Str; }; Record* CreateRecord(long Id_in, CString Str_in) {  Record* ret;  if ((ret = (Record*)malloc(sizeof(Record))) == NULL) {   return NULL;  }  ret->Id = Id_in; // (1)  ret->Str = Str_in; // (2)  return ret; } この関数のコンパイルはうまくいきますが、関数実行時にメモリ参照エラーとなります。 調べてみると、(1)のlong型変数への代入はうまくいっているのですが、(2)のCString型変数への代入がうまくいっていないようです。 既にインスタンス化されているRecord型オブジェクトへのポインタを受け取り、それに代入するという関数であればうまく動きました。 (例) void SetRecord(Record* received, long Id_in, CString Str_in) {  received->Id = Id_in;  received->Str = Str_in; } 先に示したCreateRecord関数は、どこが良くないのでしょうか。

質問者が選んだベストアンサー

  • ベストアンサー
  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.2

mallocでメモリを確保しただけでは、 ret->Strのコンストラクタが呼ばれていません。なので、ret->Strはめちゃくちゃな中身になっています。というわけで、ret->Strにアクセスすると落ちます。 C++で動的にメモリを使いたいときは、よっぽど特殊な事情がない限り基本的にはmallocではなくてnewを使ってください。

mgsinx
質問者

お礼

ご回答いただき、ありがとうございます。 なるほど、確かにmallocを使うと必要な量のメモリは確保されますが、実際にインスタンス化されたわけではなく、データ同士の相互関係等が未定義のままですね。 まだオブジェクト指向言語の経験が浅いので、もっと基礎を固めたいと思います。

その他の回答 (2)

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.3

C++ではnewを使うというのは他の回答者さんの書かれている通り。 (あわせて、freeの代わりにdeleteを使うことも忘れずに) その上で、現行のC++ではnewの確保失敗は単純にNULLが返るわけではありませんので、 C言語のようなNULLとの比較では意図通りに動きません。 例外処理を調べてtryを使ってください。 ※コンパイラのバージョンや設定にもよります。  例えば現行のC++仕様より古いVC6ならNULL比較で正しく動きます。  また、MFCには独自クラスも用意されていますので、  std::bad_allocやCMemoryExceptionをcatchします。

mgsinx
質問者

お礼

ご回答ありがとうございます。 C++ではメモリアロケーションエラーは例外処理を使えばいいのですね。 とても参考になります。 オブジェクトの削除もdeleteを使うようにします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

mallocでは領域を確保したに過ぎないのではないかと思います Record* CreateRecord(long Id_in, CString Str_in) {  Record* ret;  if ((ret = new Record()) == NULL) {   return NULL;  }  ret->Id = Id_in; // (1)  ret->Str = Str_in; // (2)  return ret; } といった具合に new演算子をつかってRecordクラスのコンストラクタを呼んでみましょう

mgsinx
質問者

お礼

ご回答いただき、ありがとうございます。 教えていただいたとおりnew演算子を使ってみたところ、うまく実行できました。 Cでのmallocは、C++ではnewを使えばいいのですね。

関連するQ&A

  • VC++におけるCStringの変換

     VisualC++6.0において、CStringというオブジェクトがありますよね。  たとえば、Edit->GetWindowText(str);  などで、strにEditのさす文字列を入れたりして、そのあとにstrを使って算術演算をするときにどうしてもstrをintかlong型に直したいのです。  キャストの仕方がよくわからないので、知っている方教えてください。よろしくお願いします。

  • CString ←→ BSTRの変換について

    次のコードのように、CString ←→ BSTRの変換を行いたいのですが、データがUTF8だと文字化けしてしまいます。 どうすれば良いでしょうか? 環境:VC++7(.net 2003)+MFC+WinXP 【結果】 CString-------------------------- FFFFFFE2 FFFFFF97 FFFFFF8B 3C BSTR-------------------------- FFFFFFE2 FFFFFF97 FFFFFF81 45 【ソース】 int intCT; int nSize = 0; BYTE *pSource = (BYTE *)"○<"; this->ConvSJistoUtf8(pSource, (BYTE *)NULL, &nSize ); BYTE* pDist = new BYTE[ nSize + 1 ]; ZeroMemory( pDist, nSize + 1 ); this->ConvSJistoUtf8(pSource, pDist, &nSize ); CString str = CString(pDist); delete []pDist; pDist = NULL; BSTR bstr; bstr=str.AllocSysString();//CString→BSTRへの変換 CString str2=CString(bstr);//BSTR→CStringへの変換 TRACE("CString--------------------------\n "); for( intCT = 0; intCT < str.GetLength(); intCT++ ) { if( intCT%16 == 0 ) TRACE("\n"); TRACE("%02X " , str[intCT]); } TRACE("\n "); TRACE("BSTR--------------------------\n "); for( intCT = 0; intCT < str2.GetLength(); intCT++ ) { if( intCT%16 == 0 ) TRACE("\n"); TRACE("%02X " , str2[intCT]); } TRACE("\n "); BOOL ConvSJistoUtf8( BYTE* pSource, BYTE* pDist, int* pSize ) { *pSize = 0; //ShiftJISからUTF-16へ変換 const int nSize = ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pSource, -1, NULL, 0 ); BYTE* buffUtf16 = new BYTE[ nSize * 2 + 2 ]; ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pSource, -1, (LPWSTR) buffUtf16, nSize ); //UTF-16からShift-JISへ変換 const int nSizeUtf8 = ::WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR) buffUtf16, -1, NULL, 0, NULL, NULL ); if( !pDist ){ *pSize = nSizeUtf8; delete buffUtf16; return TRUE; } BYTE* buffUtf8 = new BYTE[ nSizeUtf8 * 2 ]; ZeroMemory( buffUtf8, nSizeUtf8 * 2 ); ::WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR)buffUtf16, -1, (LPSTR) buffUtf8, nSizeUtf8, NULL, NULL ); *pSize = lstrlen( (char*)buffUtf8 ); memcpy( pDist, buffUtf8, *pSize ); delete buffUtf16; delete buffUtf8; return TRUE; }

  • オブジェクトの消滅と関数の戻り値オブジェクト

    public sub test1() { dim hoge as classX = tes2() test2.getHogehoge() } public Function test2() as classX { dim ret As New classX("初期化") return ret } 大変簡略化してますが上記2つの関数があった場合 関数test1でclassX型のアドレスhogeに、test2()が返す classX型オブジェクトの参照を代入しています。 でこの実体というかインスタンスretはtest2()の中の スコープで消滅する自動変数として生成されています。 んで実際関数test2を抜けるときretのインスタンスは 破棄されるのにtest1ではそれをhogeアドレスに代入して 使用していいのでしょうか?

  • CString型 全角半角を意識せずに「1文字」ずつ取り出す

    CString型の文字列に格納されている文字を1文字ずつ取り出したいです。 ただし半角なら1バイト単位で、全角なら2バイト単位で、という風に分離したいです。 半角だけなら、str[0] str[1]...という風に取り出せますが、 全角が混じっていると、1バイト目、2バイト目と分離されてしまいます。 その文字が半角か全角かを判断して、半角なら1バイト、全角なら2バイト同時に取り出すロジックを、下記のような感じの関数として作りたいです。 CString ripString(CString str,int index){ //ソースとなる文字列、n文字目 /*~処理~*/ return 文字列; } たとえば"あaいbうcえdおe"という文字列を入れると、 CString str="あaいbうcえdおe"; ripString(str,0) →結果 "あ" ripString(str,1) →結果 "a" ripString(str,2) →結果 "い" ripString(str,3) →結果 "b"  ・  ・  ・   こういうことをするのに良い方法はありますか? 1バイトごとのそれぞれの文字自身が、 ・半角文字なのか ・全角文字の前1バイトなのか ・全角文字の後1バイトなのか これをプログラム的に判別する方法があればいいのですが・・・悩んでいます。

  • 関数について

    char *str_char(const char *str, int c) { while (*str++) if (*str == c) return ((char *)str); return (NULL); } /*文字列strから文字cを検索し最初に存在する文字へのポインターを返す*/ 上記関数なのですが 5行目を return (str); にしてはなぜいけないのでしょうか。

  • char*の実体の数値をchar変数に格納する方法

    ある関数の戻り値がchar*でその関数の戻り値をchar変数に格納したいです。 char *ret_ch() { char *p="12"; return p; } int main(){ char res; //ここでret_ch()の戻り値の実体数値を代入 res=*ret_ch(); cout<<"RET=="<<ret<<endl; return 0; } char型は1バイトなので一文字しか入りません。 char型に数値として扱い、上記のポインターの 実体数値を格納するにはどのようにしたらいいのでしょうか? よろしくお願い致します。

  • OpenCLを用いた"Hello"の表示

    こんにちは、OpenCLの勉強をしている学生です。 OpenCL入門という本を読み、Helloを表示させるサンプルプログラムを実行させたのですがMacbookPro上のMacOSXでは動くのに対し、自作マシンのWindows7上ではコンパイルは通るがうまく動作しないという問題でて、コードをいろいろ書き換えたのですができず悩んでいます。 大まかな環境はそれぞれ以下の通りです。 MacBookPro OS:snow leopard 型番:MacBook Pro 2260/13.3 MB990J/A Xcodeにてビルド 自作マシン OS:Windows7 Enterprise(64bit) CPU:Core2Quad Q9550 GPU:Geforce GTX260 memory:8G visual studio2008 Express Editionにてビルド 1)元のコードは以下の通りです #include"hello.h" #define MEM_SIZE (128) #define MAX_SOURCE_SIZE (0x100000) int main(){ cl_device_id device_id=NULL; cl_context context=NULL; cl_command_queue command_queue=NULL; cl_mem memobj=NULL; cl_program program=NULL; cl_kernel kernel=NULL; cl_platform_id platform_id=NULL; cl_uint ret_num_devices; cl_uint ret_num_platforms; cl_int ret; char string[MEM_SIZE]; FILE *fp; char fileName[]="./hello.cl"; char *source_str; size_t source_size; //カーネルを含むソースコードをロード fp=fopen(fileName,"r"); if(!fp){ fprintf(stderr,"Failed to load kernel.\n"); exit(1); } source_str=(char*)malloc(MAX_SOURCE_SIZE); source_size=fread(source_str,1,MAX_SOURCE_SIZE,fp); fclose(fp); //プラットフォーム、デバイス情報の取得 ret= clGetPlatformIDs(1,&platform_id,&ret_num_platforms); ret = clGetDeviceIDs(platform_id,CL_DEVICE_TYPE_DEFAULT,1,&device_id,&ret_num_devices); //OpenCLコンテキストの作成 context=clCreateContext(NULL,1,&device_id,NULL,NULL,&ret); //コマンドキューの作成 command_queue=clCreateCommandQueue(context,device_id,0,&ret); //メモリバッファの作成 memobj= clCreateBuffer(context,CL_MEM_READ_WRITE,MEM_SIZE * sizeof(char),NULL,&ret); //読み込んだソースからカーネルプログラムを作成 program=clCreateProgramWithSource(context,1,(const char **)&source_str,(const size_t *)&source_size,&ret); //カーネルプログラムをビルド ret = clBuildProgram(program,1,&device_id,NULL,NULL,NULL); //OoenCLカーネルの作成 kernel = clCreateKernel(program,"hello",&ret); //OpenlCL カーネル引数の設定 ret = clSetKernelArg(kernel,0,sizeof(cl_mem),(void *)&memobj); //OpenCLカーネルを実行 ret = clEnqueueTask(command_queue,kernel,0,NULL,NULL); //メモリバッファから結果を取得 ret = clEnqueueReadBuffer(command_queue,memobj,CL_TRUE,0,MEM_SIZE * sizeof(char),string,0,NULL,NULL); //結果の表示 puts(string); //終了処理 ret=clFlush(command_queue); ret=clFinish(command_queue); ret=clReleaseKernel(kernel); ret=clReleaseProgram(program); ret=clReleaseMemObject(memobj); ret=clReleaseCommandQueue(command_queue); ret=clReleaseContext(context); free(source_str); return 0; } 結果は MacbookPro:Hello Windows7:fpに値がなぜか代入されず(fopenがうまく動いてない?)途中(fread)でアクセス違反 2)また、fopen等がだめならと思い、その部分(カーネルを含むソースコードをロード)のコードを以下のように書き直しビルドしました。   fstream ifs(fileName,ios::in); if(ifs.fail()){ fprintf(stderr,"Failed to load kernel"); exit(1); } source_str=(char*)malloc(MAX_SOURCE_SIZE); std::string strbuf,strsrc; while(!ifs.eof())//ファイルの最後まで { ifs.getline(source_str,MAX_SOURCE_SIZE); strsrc+=source_str; } source_str=(char*)strsrc.c_str(); source_size=(int)strsrc.length(); 結果は MacBookPro:Hello Windows7:フフフフフフ‥‥ こちらはデバックするとsource_strなどにはうまく文字列が入っているのですが、kernelに値がはいっていない(0x00000)のです。どうしてそんなことが起きるのか、どう直せばいいかさっぱりです。 できれば1)を少しだけ改良して直したいと思っているのですが、2)の部分も何が原因か分かると嬉しいです。 どうか知恵をお貸しください。よろしくお願いします。

  • 文字列strの中から文字cを探すプログラム(C言語)がわからない

    文字列strの中から文字cを探すプログラム(C言語)がわからない 柴田望洋さんの「[新版]明解C言語」という本の演習11-2なんですがどうしてもわかりません。間違いは無いと思うのにコンパイルすると警告を吐かれます。 僕が書いたプログラムを載せます。 /* 文字列strの中に、文字cが含まれていれば(複数ある場合は、最も先頭側とする)、 その文字へのポインタを返し、含まれていなければNULLを返す関数 char *str_chr(const char *str, int c) {} を作成せよ。 */ #include<stdio.h> char *str_chr(const char *str, int c){ while(*str){ if(*str==c) return str; str++; }     return NULL; } int main(){ char *str; char c; scanf("%s",str); scanf(" %c",c);     printf("%d",str_chr(str,c)); return 0; } コンパイラは「関数str_chrのif分の中のreturn strの型変換に問題がある」と言っているんです。 型変換はしるつもりは無いのにコンパイラはなぜそのように認識するのでしょうか。 またネット答えを探しましたがどうやらこのreturn strの部分はreturn (char*)strが正解のようです。意味がわかりません。strはポインタなのになぜまたわざわざchar型に変換しているのですか?といか(char*)の意味が根本的にわかりません。 質問ばかりですみません。初心者でポインタがどうにも理解できないんです。 誰か詳しい人教えてください。 お願いします。

  • 配列を返す

    ファイルから読み込んだ一行の文字列を読み込みカンマごとに区切って 返すというプログラムを関数化することで効率を図りたいと思います。 int main() { char buf[1000]; char *str; char *bufG; //ファイルを読み込む  while(fgets(buf,1000,fp) != NULL){//一行ずつ読む str = buf;//先頭アドレスを指す     bufG = //文字列を返す関数  ・  ・  ・ } } //文字列を返す関数 {    for(i = 0; *str != ',' && *str != '\0'; i++){ if(*str == '\n'){ bufG[i] = '\0'; } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; return bufG; } 前の質問で自動変数でこの関数を抜けたら廃棄になるというのは わかったんですが(そういう警告がでました) ここからどのようにすれば求めるプログラムになりますか? 引数とかちょとわからないので関数定義を書きませんでした。 (1)ファイルをよみこむ (2)一行ずつ読み込み文字列をbufにいれる (3)ポインタstrをbufの先頭アドレスにする (4)get_word関数にてポインタをずらしていき カンマがあればそこまでの文字列を返す (5)main関数に戻り変数に代入する (6)終端文字があるまで(4)ー(5)を繰り返す。 (7)さらに行数分繰り返す これらの一連の流れをやりたいのですが わかりません。

  • array_mapの再帰処理がうまく行かない

    長文で失礼します。 array_mapでの再帰処理がうまく行かないのでどこが間違っているか教えてください。 まず、このような配列があります。配列の中に配列があります。 $ary = array(1, 2, null, array("a", null, "c")); この配列の中のnullを"なし"という文字列に変換したいです。 array_mapを使って再帰的にやってみました。 まずはうまく行ったコードから。 ------------------------------------------------------ $ary = array(1, 2, null, array("a", null, "c")); var_dump(null2Nashi($ary)); // nullを"なし"に置換する関数 function null2Nashi(  $in_array ){  if(is_array($in_array)){   return array_map("null2Nashi", $in_array);  } else {   if ($in_array === null){    $in_array = "なし";   }   return $in_array;  } } ------------------------------------------------------ 結果はnullが"なし"に変換されました array (size=4)  0 => int 1  1 => int 2  2 => string 'なし' (length=6)  3 =>   array (size=3)    0 => string 'a' (length=1)    1 => string 'なし' (length=6)    2 => string 'c' (length=1) そしてこの"なし"をコード内で指定するのではなく引数で指定したいと思って無名関数を使って以下のコードにしました。 ------------------------------------------------------ $ary = array(1, 2, null, array("a", null, "c")); var_dump(null2Str($ary, "なし")); // nullを指定文字列に置換する関数 function null2Str(  $in_array, // null値を含む配列  $in_str // null値を変換したい文字列 ){  $n = function($n_array) use($in_str){   if(is_array($n_array)){    return array_map($n, $n_array); //…(1)   } else {    if ($n_array === null){     $n_array = $in_str;    }    return $n_array;   }  };  return $n($in_array); } ------------------------------------------------------ 結果はnullは何も変換されませんでした。 array (size=4)  0 => int 1  1 => int 2  2 => null  3 =>   array (size=3)    0 => string 'a' (length=1)    1 => null    2 => string 'c' (length=1) どうやら(1)のarray_mapが動作していないようです。要素を分解せずに$nの無名関数に渡さずにそのまま第2引数を返しているだけみたいです。 何か対応方法があるでしょうか? どうぞよろしくお願い致します。

    • ベストアンサー
    • PHP