• 締切済み

delete[]と、delete演算子の明確な違いとその使い分けについて

C++の delete[]と、delete演算子の明確な違いとその使い分けについて 型* ptr = new 型[要素数]; でnewしたものについては、 delete[] ptr; にて、開放 型* ptr = new 型(); でnewしたものは、 delete ptr; するものだと、覚えてました、 特に深くは考えていなかったのですが。 std::auto_ptr<T> では、内部的には、delete[]ではなく deleteをしているという文章を見ました。 { std::autoptr<型A> iptr; iptr = new 型A; } のようなときは いいのですが・・・ { std::auto_ptr<char> cptr; cptr = new char[n+1]; } のようなときは、 動的配列なので、内部的に delete ptr; になってしまいます。 このように、テンプレートクラスを自作するときに 実際の特殊な型が決定されるまで、 deleteで、いくのかdelete[]でいくのか決定できないのはつらそうです。 そこで、deleteと、delete[]の正しい意味を 知りたいのですが。 Javaや、.NETには、配列オブジェクトというものが あります。 C++で、 new char[n+1]; は配列オブジェクトでしょうか? だとすると、delete[]は 右に来たものが配列オブジェクトなら、 Lengthを(こんなものがあるとして)調べて 1つづつ、まわして、deleteのほうを使ってひとつひとつ開放していく。 右に来たものが配列オブジェクトでなければ deleteと同じ振る舞いをする。 という動きが物理的に考えれそうで。 delete[]と書いておけば、状況に応じて、 動的配列なら全要素サーチして、開放そうでなければ deleteという風に、勝手に自動でそうしてくれるなら、 どんなときでも、delete[]にするようにコードを書いておけばよさそうです。。 実際のところどうなんでしょうか? deleteと、delete[]の明確な意味の違いと、 使用方法の違いについて教えてください。

みんなの回答

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.3

> new char[n+1]; > は配列オブジェクトでしょうか? JIS X3014:2003の5.3.4 new式の段落5には、「割付けたオブジェクトが配列の場合」という記述が出てくるので、一応は配列オブジェクトなんだと思います。ただし、Javaや.NETの同名のそれとは別物です。 (C/C++における「オブジェクト」は、概ね「変数」の意味です) > 右に来たものが配列オブジェクトでなければ > deleteと同じ振る舞いをする。 これを実現するためには、配列かどうかを調べるためのフラグをどこかに仕込んでおく必要があります。つまり、それだけオーバーヘッドになるわけです。そんな機能が必要なら、そういうスマートポインタを自作した方がいいです。 auto_ptrでは、確かに template <typename T> class auto_ptr<T[]>; というのを特殊化してやれば、(delete式に関しては)配列も扱えそうな気がしますが、添え字演算子の多重定義をするかどうかなど、セマンティクスが変わるので、あまり有益ではありませんね。

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

> std::auto_ptr<char> cptr; cptr = new char[n+1]; この例なら、std::string (または std::vectorあたり)が適当では? new[]は主にC言語との互換用/最適化用のためにあるもので、 動的配列はSTLのコンテナ等を使えばよいもの。auto_ptrは単体オブジェクト専用です。 > deleteで、いくのかdelete[]でいくのか決定できないのはつらそうです。 RAIIとか考慮したC++ソースではdelete/delete[]って自分で呼ぶことはほぼ無いですし、 単複がブレても定義と初期化(new/new[])だけの問題ですから、ここは多分間違えないでしょう。 知らない初心者がハマるとか、うっかり間違え易い点は同意ですが、 自分でdeleteするようなコードは慣れてから書けばよく、 初心者がdelete/delete[]を自分で書くのが間違ってると私は思います。 # 生メモリの操作を素人にSTLより先に教えるのがおかしいと思う。 > は配列オブジェクトでしょうか? C++に配列というオブジェクトはありません。 > という動きが物理的に考えれそうで。 そうすることもできたでしょうが、自動でこういう動きはしません。 > どんなときでも、delete[]にするようにコードを書いておけばよさそうです。。 言語設計がnew/delete、new[]/delete[]のペア前提なので、 newでは要素数等を保持せずnew[]でのみ保持する実装が可能です。 つまり、現状C++では問題が出る可能性があり、やってはいけません。 少しでも無駄な処理/メモリを削れる設計のためでしょう。(そういう言語です>C++) > どんなときでも、delete[]にするようにコードを書いておけばよさそうです。。 現行C++でこれを実現したい場合、new T;を使わず常に new T[1];とすることはできます。 # C++ ではnew[]の他に、単体専用に効率のよいnewも用意されていると考えたらどうでしょう。

lawson
質問者

お礼

>この例なら、std::string (または std::vectorあたり)が適当では? std::auto_ptr<char> cptr; なんぞ使わず、普通に std::stringで問題ないですね。 >動的配列はSTLのコンテナ等を使えばよいもの。>auto_ptrは単体オブジェクト専用です。 そうでした。コンテナを使えば単体オブジェクト 専用でも気にする必要もなかったです。 >少しでも無駄な処理/メモリを削れる設計のためでしょう。(そういう言語です>C++) そういう言語なんですね。 >現行C++でこれを実現したい場合、new T;を使わず常に new T[1];とすることはできます。 なるほど、そこまですることもないですが。 どうしても、deleteとdelete[]の違いのせいで、 テンプレートが作れないで悩むような時が あったら、その 「new T[1];」も1つの選択肢として覚えておきます。 ># C++ ではnew[]の他に、単体専用に効率のよいnewも>用意されていると考えたらどうでしょう。 それでわざわざ、2種類あるんですね。 なんのために、いちいち使い分けてんだろ。 めんどくせーとおもってましたが。 これで納得しました。

回答No.1

> new char[n+1]; > は配列オブジェクトでしょうか? NO. delete も delete[] もメモリの解放という意味では同じです。 確保された領域を一気に解放します(部分的に解放できません)。 # mallocでどれだけ確保してあろうがfreeで解放されるのと同様。 異なるのはデストラクタを1回動かすか、それとも"確保された領域/一つのインスタンスが占める大きさ"回動かすか、の違いとなります。

lawson
質問者

お礼

>異なるのはデストラクタを1回動かすか、それとも"確>保された領域/一つのインスタンスが占める大きさ"回動かすか、の違いとなります。 このくだりを聞くと、 delete[]のいっぽん槍でいけそうに聞こえる。 No2さんは、それはやめたほうがよいと、 記述しています。 いづれにせよ。 単体オブジェクト専用のdeleteを内部でやってますが、コンテナを使えばよさそうなのでそうします。

関連するQ&A

  • delete演算子オーバーロードについて

    delete演算子オーバーロードする際、 デストラクタが呼び出される困っています。 例えば、 void* operator new(size_t size, const char* filename , int line , const char* funcname ); とnew演算子をグローバル定義します。 すると、意図通りnew演算子がCallされ、対応するコントラクタも 問題なくCallされます。 そして、上記new定義にペアとなるdelete演算子もグローバル定義します。 void operator delete(void* pMem, const char* filename , int line , const char* funcname ); 通常どおりdeleteで呼び出してしまうと、標準のdeleteがcallされてしまうため、 #define MYELETE(s) operator delete( (void*)(s) , __FILE__ , __LINE__ , __FUNCTION__ ) 上記のようなカスタムマクロを定義してCallしています。 オーバーロード定義されたアドレスがCallされるところまで、 意図通りなのですが、肝心のデストラクタがCallされません。 型が判明している場合、 単体でデストラクタを明示的に呼び出すことはできますが、 任意のポインタのデストラクタを明示的に呼び出す方法は ありますでしょうか?

  • 配列の中身のdelete

    こんにちは 配列を使った際、うまいことDeleteが働いてくれないため メモリが開放されずに困っています、 初歩的な質問で申し訳ありませんが、質問させて下さい。 下記のプログラムでは、 単純にメモリ確保とメモリ開放を行っていると思うのですが… 注釈にもあるように、Deleteでメモリが開放されません。 #include <iostream> using namespace std; class Base { }; class Sub : public Base { char size[256];//領域確保用 }; void main() { Base* obj[10000]; for(int cnt=0;cnt<10000;cnt++) { obj[cnt] = new Sub; } for(int cnt=0;cnt<10000;cnt++) { delete obj[cnt];//メモリが開放されない } } ポインタが怪しいと思うのですが、 どうにかうまいことメモリを開放することは出来ないでしょうか。 よろしければ返答お願いいたします。

  • 多次元配列の new

    多次元配列を new すると、どのような型のサイズの領域の配列が確保されるんでしょうか?たとえば、  int (*a)[2] = new int[3][2]; とすると、  1. 長さ2のintの配列へのポインタ型の長さ3の配列の領域が確保される のか、  2. int[3][2] すなわち、int が 6 つ分の領域が確保される のか。 今まで、「そりゃあ 2 の方だろう」と信じ込んであまり考えずにいたんですが、「コードの型形式からすると 1 の方の解釈でもいいよなぁ」と、ふと思ったものですから、質問させていただきました。 わたしの環境で調べてみると(配列用のハウスキーピング的な余分の領域とか、パディングなどは無視して)、確かに 1 の方なんですか、これで標準準拠なんでしょうかね?^^; XP Home Edition Ver.2002 SP2 cygwin v.1.0.2-1 GNU g++ v.4.1.1 ===== #include <iostream> #include <new> #include <cstdlib> struct A { char a; void *operator new(std::size_t s) { void *p = std::malloc(s); std::cout << "A::new(): " << p << '\t' << s << '\n'; return p; } void operator delete(void *p, std::size_t s) { std::cout << "A::delete(): " << p << '\t' << s << '\n'; if (p) std::free(p); } void *operator new[](std::size_t s) { void *p = std::malloc(s); std::cout << "A::new[](): " << p << '\t' << s << '\n'; return p; } void operator delete[](void *p, std::size_t s) { std::cout << "A::delete[](): " << p << '\t' << s << '\n'; if (p) std::free(p); } }; int main() { std::cout << sizeof(char) << '\t' << sizeof(A) << '\t' << sizeof(A(*)[8]) << '\n'; A *a = new A; std::cout << a << '\n'; A *aa = new A[8]; std::cout << aa << '\n'; A (*aaa)[8] = new A[8][8]; std::cout << aaa << '\n'; A (*aaaa)[8][8] = new A[8][8][8]; std::cout << aaaa << '\n'; delete[] aaaa; delete[] aaa; delete[] aa; delete a; } ===== % ./a.exe 1 1 4 A::new(): 0x870668 1 0x870668 A::new[](): 0x870678 12 0x87067c A::new[](): 0x870688 68 0x87068c A::new[](): 0x8706d0 516 0x8706d4 A::delete[](): 0x8706d0 516 A::delete[](): 0x870688 68 A::delete[](): 0x870678 12 A::delete(): 0x870668 1

  • new演算子のオーバーロードについて

    #include <stdio.h> #include <windows.h> class MyNew { public: void* ptr; MyNew( void* p ) { ptr = p; } void* MyNew::operator new( size_t size ) { printf("new-\n"); return malloc( size ); } void MyNew::operator delete( void* ptr ) { printf("delete-\n"); free( ptr ); } }; void main( void ) { MyNew p = new int; } クラスのメモリ確保をnew演算子のオーバーロードを用いて書いてみたのですがオーバーロードしたnew演算子が呼ばれません。 なぜでしょうか? /** VisualStdio2005コンソールアプリケーション WindowsXP */

  • オブジェクトと配列の違いはなんでしょうか?

    オブジェクトと連想配列、 その二つの型の決定的違いは、何が違うのでしょうか?

    • ベストアンサー
    • PHP
  • C++のoperator関数でのキャストする場合の書き方がまだよく理解

    C++のoperator関数でのキャストする場合の書き方がまだよく理解できていません。 下記のコードで、 //ここから #include "stdafx.h" #include <string> #include <iostream> class AutoPtr { char *ptr; public: AutoPtr():ptr(0) { } ~AutoPtr() { delete [] ptr; } // char *operator=(char *ptr) { delete [] this->ptr; this->ptr = ptr; return this->ptr; } operator char *(){ return ptr; } char &operator[](int index) { return ptr[index]; } }; void reverse(char *str) { int i, n; AutoPtr work; n = strlen(str); work = new char[n+1]; strcpy(work, str); for(i=0; i<n; i++) { str[i] = work[n-i-1]; } printf("%s\n", str); } int _tmain(int argc, _TCHAR* argv[]) { char str[] = "ABCDEFG"; reverse(str); return 0; } //ここまで 2番目のoperator関数の定義ですが、 operator char *(){ return ptr; } これは多分、reverse()関数中の、 strcpy(work, str); のworkの展開に用いられると思うのですが、 機能としては、「operator char *」はAutoPtrをchar *にキャストするために使われているらしいのですが、何故この書き方でAutoPtrをchar *型にキャストできるのかがいまいち分かりません。また、2番目のoperator関数の記述「operator char *()」はどこまでが型で、どこからが関数の定義と見なせばよいのでしょうか? 何か勘違いしているかもしれません。理解されている方、御教示いただければと思っています。 よろしくお願い致します。

  • C++のnew演算子で動的確保

    new()を初めて使ってみました。 エラーは無かったけど、間違いがあったら教えてください。 #include <iostream.h> main(){ int n = 0; char* str = "mojisuufumei"; char* myValue; while(!str[n])n++; myValue = new char[n+1]; strcpy(myValue, str); cout << myValue; delete []myValue; } char*型のmyValueを動的確保したつもりです。 myValue[0], myValue[1], … のそれぞれの値を unsignedにして宣言したい場合はどうしたらいいんですか? #include <iostream.h> main(){ int n = 0; char* str = "mojisuufumei"; unsigned char* myValue; while(!str[n])n++; myValue = new unsigned char[n+1]; strcpy(myValue, str); cout << myValue; delete []myValue; } だと、strcpy()のとこでコンパイルエラーでした。

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • STLを使わずに可変長配列を再現する方法

    STLのlistが(配列に比べると)想像以上に遅かったので C++で可変長配列を再現したいのですけども 配列の拡張が思った以上に遅く困っています。 毎回newではオーバーヘッドが発生しますので、 現在は配列を一定数確保しておき 足りなくなったら配列を拡張(再確保)しています。 現在の配列のアドレスを一旦退避させてdeleteし、 新たにnewで生成して復帰させるといった感じです。 ただしこれでは、配列の要素数が増えるほど遅くなり、 オブジェクトの参照ならまだしも実体の場合は 全てコピーしなければならないので、 場合によってはSTLのlistよりも遅くなってしまいます。 newで生成してるのでできればreallocは使わずに 再現したいのですが、どうにか方法は無いでしょうか? よろしくおねがいします。 //----------------------------------------------- struct Test {   int val;   Test( int _val ){ val=_val; } }; Test obj1( 1 ); Test obj2( 2 ); Test obj3( 3 ); // 元のデータに代入 Test **ptr = new Test*[2]; ptr[0] = &obj1; ptr[1] = &obj2; // 退避させる Test **tmp = new Test*[2]; for( int i=0; i<2; i++ ) tmp[i] = ptr[i]; // 拡張する delete [] ptr; ptr = new Test*[4]; // 復帰させる for( int i=0; i<2; i++ ) ptr[i] = tmp[i]; delete [] tmp; ptr[2] = &obj3; //----------------------------------------------- ※NULLチェックなどはここでは省いています。

  • new演算子で領域を確保した変数の初期化

    ポインタの配列の各要素にnewで領域を与えたとき、 char *str[3]; str[0] = new char[10]; str[1] = new char[5]; str[2] = new char[10]; str[0]~str[2]をNULLで初期化するには どのようにすれば良いのでしょうか? よろしくお願いします。

専門家に質問してみよう