転載元:https://www.cnblogs.com/QG-whz/p/5140930.html
序文
数週間前、私はC ++ R&Dのインターンシップのために面接に行きました。面接官は質問をしました:
newとmallocの違いは何ですか?
これはよくある質問です。そのとき、newは空きストレージ領域からメモリを割り当て、mallocはヒープからメモリを割り当てると返信しました。new/ deleteはコンストラクタ/デストラクタを呼び出してオブジェクトを初期化および破棄します。演算子new / deleteはオーバーロードできます。その後強制されます。分析空きストレージ領域とヒープの違いを見てみましょう。私が戻った後、実際にはnewとmallocの間に多くの違いがあるため、この質問は実際にはあまりよく答えられていないと感じました。面接では、たまたま最終試験の直後だったので、いくつかのコースを組む時間がありませんでした。今日、私はこの質問を整理するのに少し時間を費やしました。
newとmallocの10の違い
1.要求されたメモリの場所
オブジェクトにメモリスペースを動的に割り当てるためのフリーストア(フリーストア)のnew演算子、スタックのmalloc関数がメモリを動的に割り当てます。空きストレージ領域は、new演算子に基づくC ++の抽象的な概念であり、new演算子を介したメモリアプリケーションはすべて空きストレージ領域です。ヒープはオペレーティングシステムの用語であり、プログラムメモリを動的に割り当てるためにオペレーティングシステムによって維持される特別なメモリです。C言語はmallocを使用してヒープからメモリを割り当て、割り当てられた対応するメモリを解放します。
次に、空きストレージ領域をヒープにすることができるかどうか(質問は、newがヒープにメモリを動的に割り当てることができるかどうかと同じです)は、演算子newの実装の詳細によって異なります。空きストレージ領域は、ヒープだけでなく静的ストレージ領域にすることもできます。これはすべて、演算子newがオブジェクトにメモリを割り当てる場所によって異なります。
特に、newはオブジェクトにメモリを割り当てさえしないかもしれません!newを配置する機能は、これを行うことができます。
new (place_address) type
place_addressは、メモリの一部のアドレスを表すポインタです。上記のタイプを使用して1つのアドレスのみでnew演算子を呼び出す場合、new演算子は特別な演算子newを呼び出します。これは次のバージョンです。
void * operator new (size_t,void *) //不允许重定义这个版本的operator new
この演算子newはメモリを割り当てず、ポインタ引数を返すだけです。次に、適切な新しい式が、place_addressで指定されたアドレスでのオブジェクトの初期化を担当します。
2.戻り値の型の安全性
new演算子のメモリ割り当てが成功すると、オブジェクト型のポインタが返されます。型はオブジェクトと厳密に一致し、型変換は必要ありません。したがって、newは型の安全性に準拠した演算子です。mallocメモリ割り当ては成功しますが、void *を返し、void *ポインタを強制的な型変換によって必要な型に変換する必要があります。
型安全性は、メモリの安全性とほぼ同等です。型安全性コードは、許可されていないメモリ領域にアクセスしようとはしません。C ++の型安全性については多くのことが言えます。
3.メモリ割り当てに失敗した場合の戻り値
新しいメモリ割り当てが失敗すると、bac_alloc例外がスローされ、NULLは返されません。メモリ割り当てが失敗すると、mallocはNULLを返します。
C言語を使用する場合、mallocがメモリを割り当てた後、割り当てが成功したかどうかを判断することに慣れています。
int *a = (int *)malloc ( sizeof (int ));
if(NULL == a)
{
...
}
else
{
...
}
C言語からC ++キャンプへの新規参入者は、この習慣をC ++に持ち込む可能性があります。
int * a = new int();
if(NULL == a)
{
...
}
else
{
...
}
実際、newはNULLをまったく返さないため、これを行う意味はありません。プログラムは、メモリ割り当てが成功したことをifステートメントが示すまで実行でき、失敗した場合は例外がスローされます。正しいアプローチは、例外メカニズムを使用することです。
try
{
int *a = new int();
}
catch (bad_alloc)
{
...
}
ちなみに、例外の基本を理解したい場合は、http://www.cnblogs.com/QG-whz/p/5136883.html C ++例外メカニズム分析を参照してください。
4.メモリサイズを指定する必要がありますか
new演算子を使用してメモリ割り当てを適用する場合、メモリブロックのサイズを指定する必要はありません。コンパイラはタイプ情報に基づいてそれを計算し、mallocは必要なメモリのサイズを明示的に示す必要があります。
class A{...}
A * ptr = new A;
A * ptr = (A *)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A);
もちろん、mallocを使用してカスタムタイプにメモリを割り当てることは適切ではありません。次のタイプを参照してください。
5.コンストラクタ/デストラクタを呼び出すかどうか
new演算子を使用してオブジェクトメモリを割り当てる場合、次の3つの手順があります。
- 最初のステップ:演算子new関数(配列の場合は演算子new [])を呼び出して、特定のタイプのオブジェクトを格納するのに十分な大きさのプリミティブな名前のないメモリスペースを割り当てます。
- 2番目のステップ:コンパイラーは、対応するコンストラクターを実行してオブジェクトを作成し、その初期値を渡します。
- 3番目の部分:オブジェクトが構築された後、オブジェクトへのポインターが返されます。
削除演算子を使用してオブジェクトのメモリを解放する場合、次の2つの手順があります。
- 最初のステップ:オブジェクトのデストラクタを呼び出します。
- ステップ2:コンパイラーは、演算子delete(または演算子delete [])関数を呼び出して、メモリー・スペースを解放します。
つまり、new / deleteは、オブジェクトのコンストラクタ/デストラクタを呼び出して、オブジェクトの構築/デストラクタを完成させます。Mallocはそうではありません。あまり長くない場合は、私の例を見てください。
class A
{
public:
A() :a(1), b(1.11){}
private:
int a;
double b;
};
int main()
{
A * ptr = (A*)malloc(sizeof(A));
return 0;
}
戻り時にブレークポイントを設定し、ptrが指すメモリの内容を監視します。
データメンバーaとbの値が初期化されていないため、Aのデフォルトコンストラクターが呼び出されていないことがわかります。これが、malloc / freeを使用してC ++カスタムタイプを処理することはできないと言った理由です。適切であり、実際、それはカスタムタイプ以上のものであり、構築/破棄する必要がある標準ライブラリ内のすべてのタイプはすべて不適切です。
newを使用してオブジェクトを割り当てる場合:
int main()
{
A * ptr = new A;
}
プログラムによって生成されたアセンブリコードを見ると、Aのデフォルトコンストラクタが呼び出されていることがわかります。
6.配列の処理
C ++は、配列型を処理するためにnew []とdelete []を提供します。
A * ptr = new A[10];//分配10个A对象
new []を使用して割り当てられたメモリは、delete []を使用して解放する必要があります。
delete [] ptr;
Newの配列のサポートは、コンストラクター関数を呼び出して各配列要素を初期化し、オブジェクトが解放されたときに各オブジェクトのデストラクタを呼び出すという点に反映されています。delete []はnew []と組み合わせて使用する必要があることに注意してください。そうしないと、配列オブジェクトの部分的な解放が検出され、メモリリークが発生します。
mallocに関しては、このメモリまたは他の何かに配置する配列を認識していません。とにかく、元のメモリの一部を提供し、メモリのアドレスを提供した後に実行されます。したがって、配列のメモリを動的に割り当てる場合は、配列のサイズも手動でカスタマイズする必要があります。
int * ptr = (int *) malloc( sizeof(int)* 10 );//分配一个10个int元素的数组
7.newとmallocはお互いに電話をかけることができますか
演算子new / operator deleteの実装はmallocに基づくことができ、mallocの実装はnewを呼び出すことができません。以下は、operator new / operator deleteを記述する簡単な方法であり、他のバージョンも同様です。
void * operator new (sieze_t size)
{
if(void * mem = malloc(size)
return mem;
else
throw bad_alloc();
}
void operator delete(void *mem) noexcept
{
free(mem);
}
8.オーバーロードできますか
opeartor new / operatordeleteはオーバーロードされる可能性があります。標準ライブラリは、演算子new関数と演算子delete関数の8つのオーバーロードバージョンを定義しています。
//这些版本可能抛出异常
void * operator new(size_t);
void * operator new[](size_t);
void * operator delete (void * )noexcept;
void * operator delete[](void *0)noexcept;
//这些版本承诺不抛出异常
void * operator new(size_t ,nothrow_t&) noexcept;
void * operator new[](size_t, nothrow_t& );
void * operator delete (void *,nothrow_t& )noexcept;
void * operator delete[](void *0,nothrow_t& )noexcept;
カスタマイズされたバージョンがグローバルスコープまたはクラススコープ内にある必要がある場合は、上記の関数バージョンのいずれかをカスタマイズできます。あまりにも詳細なことはここでは説明しません。要するに、演算子new / operator deleteをオーバーロードして、newおよびdeleteがオブジェクトにメモリを割り当てる方法とオブジェクトを再利用する方法を決定するのに十分な自由があることを知っています。
そして、malloc / freeはオーバーロードを許可しません。
9.直感的にメモリを再割り当てする機能
mallocによって割り当てられたメモリを使用した後、使用中にメモリが不足していることがわかった場合は、realloc関数を使用してメモリを再割り当てし、メモリを拡張できます。reallocは最初に、現在のポインタが指すメモリに十分な連続スペースがあるかどうかを判断します。ある場合は、割り当て可能なメモリアドレスを所定の位置に展開し、元のアドレスポインタを返します。スペースが十分でない場合は、最初に新しく指定されたスペースに従ってスペースを割り当てます。サイズを変更し、元のデータを変更するデータを最初から最後まで新しく割り当てられたメモリ領域にコピーしてから、元のメモリ領域を解放します。
newには、メモリを拡張するための直感的なサポート機能がありません。
10.クライアントが不十分なメモリ割り当てを処理する
演算子newは、満たされていない要件を反映するために例外をスローする前に、最初にユーザー指定のエラー処理関数(new-handler)を呼び出します。new_handlerはポインタ型です:
namespace std
{
typedef void (*new_handler)();
}
これは、エラー処理関数である、パラメーターと戻り値のない関数を指します。エラー処理関数を指定するには、クライアントはset_new_handlerを呼び出す必要があります。これは、次で宣言されている標準ライブラリ関数です。
namespace std
{
new_handler set_new_handler(new_handler p ) throw();
}
set_new_handlerのパラメーターはnew_handlerポインターであり、オペレーターnewが十分なメモリーを割り当てることができない場合に呼び出される関数を指します。戻り値もポインタであり、set_new_handlerが呼び出される前に実行されている(ただし、まもなく置き換えられる)new_handler関数を指します。
mallocの場合、クライアントは、メモリが割り当てるのに十分でない場合に何をするかをプログラムで決定できず、mallocがNULLを返すのを監視することしかできません。
総括する
上記の10の違いを表にまとめます。
特徴 | 新規/削除 | malloc /無料 |
---|---|---|
メモリを割り当てる場所 | 無料のストレージエリア | ヒープ |
メモリ割り当て成功の戻り値 | フルタイプポインタ | ボイド* |
メモリ割り当て失敗の戻り値 | デフォルトで例外をスローします | NULLを返します |
割り当てられたメモリのサイズ | タイプに基づいてコンパイラによって計算されます | バイト数は明示的に指定する必要があります |
配列の処理 | 配列を処理する新しいバージョンnew []があります | ユーザーは、配列のサイズを計算してから、メモリを割り当てる必要があります |
割り当てられたメモリの拡張 | 直感的に扱えない | reallocで簡単に完了できます |
お互いに電話するかどうか | はい、演算子new / deleteの特定の実装を参照してください | 新規に呼び出すことはできません |
メモリの割り当て中にメモリが不足しています | お客様は、処理機能を指定するか、アロケーターを再定式化することができます | ユーザーコードでは処理できません |
関数のオーバーロード | 許可する | 禁じられている |
コンストラクタとデストラクタ | 転送 | 電話しないでください |
mallocが提供するものは、原始的な土地のようなものです。成長させたいものは、自分で土地に植える必要があります。
また、newは、フィールドの作成(配列)、シードの植え付け(コンストラクター)、およびその他の使用可能な機能を提供します。
もちろん、mallocは新しいものより劣っていると言っているのではなく、独自のアプリケーションがあります。C ++などのOOPを強調する言語では、当然、new / deleteを使用する方が適切です。