私はC++でデータを割り当てて解放する複数の方法があり、malloc
を呼び出すときはfree
を呼び出す必要があり、new
演算子を使用するときはdelete
とペアにする必要があることを理解しています(たとえば、free()
の呼び出しnew
演算子を使用して作成されたものについて)、しかし、malloc
/free
をいつ使用し、実際の世界のプログラムでnew
/delete
を使用する必要があるのかは明確ではありません。
あなたがC++の専門家である場合は、この点で従う経験則や慣習を教えてください。
Cを使わざるをえないのなら、使わないmalloc
を使うべきです。常にnew
を使用してください。
大量のデータが必要な場合は、次のようにしてください。
char *pBuffer = new char[1024];
これは正しくありませんが、注意してください。
//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;
代わりに、データの配列を削除するときにこれをするべきです:
//This deletes all items in the array
delete[] pBuffer;
new
キーワードはそれを行うC++の方法であり、それはあなたの型がそのコンストラクタが呼ばれるを持つことを保証するでしょう。 new
キーワードもmore type-safeですが、malloc
は全くタイプセーフではありません。
malloc
を使うことが有益であると私が考えることができる唯一の方法は、あなたがデータのあなたのバッファのサイズを変更するを必要とするならばだろう。 new
キーワードには、realloc
のような類似の方法はありません。 realloc
関数はもっと効率的にあなたのためにメモリの塊のサイズを拡張することができるかもしれません。
new
/free
とmalloc
/delete
を混在させることはできません。
注:この質問のいくつかの答えは無効です。
int* p_scalar = new int(5); // Does not create 5 elements, but initializes to 5
int* p_array = new int[5]; // Creates 5 elements
簡単に言えば、C++にmalloc
を使わないでください。 malloc
をC++で使用すると、new
を克服するために定義されていた多くの欠点があります。
malloc
は、意味のある意味で型保証されていません。 C++では、void*
からの戻り値をキャストする必要があります。これは潜在的に多くの問題を引き起こす。
#include <stdlib.h>
struct foo {
double d[5];
};
int main() {
foo *f1 = malloc(1); // error, no cast
foo *f2 = static_cast<foo*>(malloc(sizeof(foo)));
foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad
}
それよりも悪いです。問題の型が POD(plain old data) の場合、最初のf2
と同じように、malloc
を使用してメモリを割り当てることができます。例です。
型がPODの場合でもそれほど明確ではありません。特定の型がPODから非PODに変更されてもコンパイラエラーが発生せず、問題をデバッグするのが非常に困難であるという事実は重要な要素です。例えば、誰か(おそらく他のプログラマ、メンテナンス中に、foo
がPODではなくなるような変更を加えた場合)コンパイル時に明らかなエラーは表示されません。
struct foo {
double d[5];
virtual ~foo() { }
};
明らかな診断なしに、f2
のmalloc
も悪くなるでしょう。ここの例は些細なことですが、誤って(POD以外のメンバーを追加することによって、基本クラスで)POD以外のものを誤って導入する可能性があります。 C++ 11/boostを持っているなら、is_pod
を使ってこの仮定が正しいことをチェックし、そうでなければエラーを生成することができる:
#include <type_traits>
#include <stdlib.h>
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
return static_cast<foo*>(malloc(sizeof(foo)));
}
Boostは C++ 11や他のコンパイラ拡張なしでは型がPOD であるかどうか判断できないが。
割り当てに失敗した場合、malloc
はNULL
を返します。 new
はstd::bad_alloc
をスローします。後でNULL
ポインタを使用した場合の動作は未定義です。例外がスローされ、エラーの原因からスローされた場合、例外は明確な意味を持ちます。呼び出しのたびにmalloc
を適切なテストでラップするのは面倒でエラーが発生しやすいようです。 (あなたはすべてのその良い仕事を元に戻すために一度だけ忘れなければならない)。例外は、NULL
が意味を成して返すのがはるかに難しいので、呼び出し側がそれを賢明に処理することができるレベルに伝播することを許されることができます。 safe_foo_malloc
関数を拡張して、例外をスローするかプログラムを終了するか、または何らかのハンドラを呼び出すことができます。
#include <type_traits>
#include <stdlib.h>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
foo *mem = static_cast<foo*>(malloc(sizeof(foo)));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return mem;
}
基本的にmalloc
はCの機能で、new
はC++の機能です。その結果malloc
はコンストラクタとうまく動作しません、それはバイトの塊を割り当てることを見るだけです。配置new
を使用するようにsafe_foo_malloc
をさらに拡張することができます。
#include <stdlib.h>
#include <new>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
void *mem = malloc(sizeof(foo));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)foo();
}
私たちのsafe_foo_malloc
関数はあまり一般的ではありません - 理想的には私たちはfoo
だけではなく、どんな型でも扱うことができるものが欲しいのです。これは、デフォルト以外のコンストラクタ用のテンプレートと可変数テンプレートを使って実現できます。
#include <functional>
#include <new>
#include <stdlib.h>
void my_malloc_failed_handler();
template <typename T>
struct alloc {
template <typename ...Args>
static T *safe_malloc(Args&&... args) {
void *mem = malloc(sizeof(T));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)T(std::forward(args)...);
}
};
しかし、これまでに確認したすべての問題を解決するために、デフォルトのnew
演算子を実際に再発明しました。あなたがmalloc
と配置new
を使うつもりならば、あなたは最初からnew
を使うこともできます。
[16.4]信頼できる古いmalloc()の代わりにnewを使うべきなのはなぜですか?
FAQ:new/deleteはコンストラクタ/デストラクタを呼び出します。 newは型保証されていますが、mallocは安全ではありません。 newはクラスによってオーバーライドすることができます。
FQA:FAQで言及されているnewの美徳は美徳ではありません。コンストラクタ、デストラクタ、および演算子のオーバーロードはゴミです(ガベージコレクションがない場合の動作を参照してください)。ちょっとここで(通常、mallocによって返されたvoid *を正しいポインタ型にキャストして型付けされたポインタ変数に割り当てる必要がありますが、これは面倒ですが、「安全ではない」とは言い難い場合があります)。
ああ、そして信頼できる古いmallocを使うことで、同じくらい信頼できる&古いreallocを使うことが可能になります。残念ながら、私たちは光沢のある新しい演算子を更新するなどしていません。
それでも、言語がC++であっても、newは、言語全体で使用される一般的なスタイルからの逸脱を正当化するのに十分なほど悪くはありません。特に、単純なオブジェクトをmallocしただけでは、自明でないコンストラクタを持つクラスは致命的な動作をします。それでは、なぜコード全体でnewを使用しないのですか?人々がオペレータnewを過負荷にすることはめったにないので、おそらくそれはあなたのやり方ではあまりやり過ぎないでしょう。そして、彼らが新しく過負荷になった場合、あなたはいつでも彼らにやめるように頼むことができます。
すみません、私はただ抵抗できませんでした。 :)
C++では常にnewを使用してください。型なしのメモリブロックが必要な場合は、operator newを直接使用できます。
void *p = operator new(size);
...
operator delete(p);
Cによって管理されるメモリを割り当てるには、malloc
およびfree
onlyを使用します。中心のライブラリとAPI。あなたがコントロールするものすべてにnew
とdelete
(そして[]
の変種)を使ってください。
new vs malloc()
1)new
はoperator、malloc()
はfunctionです。
2)new
はconstructorsを呼び出しますが、malloc()
は呼び出しません。
3)new
はexactデータ型を返し、malloc()
はvoid *を返します。
4)new
はNULL(失敗した場合にスローされます)を決して返しませんが、malloc()
はNULLを返します。
5)malloc()
はできるがnew
では処理されないメモリの再割り当て
あなたの質問に答えるために、あなたはmalloc
とnew
の違いを知っているべきです。違いは簡単です。
malloc
はメモリを割り当てますが、new
はメモリを割り当て、メモリを割り当てているオブジェクトのコンストラクタを呼び出します。
ですから、Cに制限されているのでなければ、特にC++オブジェクトを扱うときは、決してmallocを使用しないでください。それはあなたのプログラムを破るためのレシピでしょう。
free
とdelete
の違いも全く同じです。違いは、delete
はメモリの解放に加えて、あなたのオブジェクトのデストラクタを呼び出すということです。
malloc
とnew
には大きな違いがあります。 malloc
はメモリを割り当てます。 Cでは、メモリの塊がオブジェクトであるため、これはCでは問題ありません。
C++では、POD型(C型に似ている)を扱っていないのであれば、実際にそこにオブジェクトがあるようにするには、メモリ位置でコンストラクタを呼び出す必要があります。多くのC++機能でオブジェクトが自動的に非PODになるため、非POD型はC++で非常に一般的です。
new
はメモリを割り当て、はそのメモリ位置にオブジェクトを作成します。非POD型の場合、これはコンストラクターを呼び出すことを意味します。
あなたがこのような何かをするならば:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
取得したポインタはオブジェクトを指していないため、参照解除できません。使用する前にコンストラクタを呼び出す必要があります(これは配置new
を使用して行われます)。
一方で、あなたがするならば:
non_pod_type* p = new non_pod_type();
new
がオブジェクトを作成したので、あなたは常に有効なポインタを得ます。
PODタイプの場合でも、この2つの間には大きな違いがあります。
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
malloc
によって作成されたPODオブジェクトは初期化されていないため、このコードは未指定の値を出力します。
new
を使うと、呼び出すコンストラクタを指定することができ、したがって明確に定義された値を取得できます。
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
本当に必要な場合は、未初期化のPODオブジェクトを取得するためにnew
を使用できます。詳しくは この他の答え をご覧ください。
もう1つの違いは、失敗時の動作です。メモリの割り当てに失敗すると、malloc
はnullポインタを返し、new
は例外をスローします。
前者は使用する前に返されたすべてのポインタをテストする必要がありますが、後者は常に有効なポインタを生成します。
これらの理由から、C++コードではnew
ではなくmalloc
を使用する必要があります。しかしそれでも、new
を "in the open"で使用しないでください。後でリリースする必要があるリソースを取得するためです。 new
を使用するときは、その結果をただちにリソース管理クラスに渡す必要があります。
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
new
がmalloc
で行わないことがいくつかあります。
new
は、そのオブジェクトのコンストラクタを呼び出すことによってオブジェクトを構築しますnew
は割り当てられたメモリの型キャストを必要としません。ですから、もしあなたがmalloc
を使うのであれば、上記のことを明示的に行う必要があります。さらに、new
はオーバーロードすることができますが、malloc
はオーバーロードすることはできません。
あなたが構築/破壊を必要とせず、再割り当てを必要とするデータ(例えば、たくさんのint)を扱うのであれば、malloc/freeがreallocを与えるので良い選択だと思います。 -delete(これは私のLinuxマシンにありますが、これはプラットフォームに依存するかもしれないと思います)。 PODではないC++オブジェクトを使用していて、構築/破棄が必要な場合は、new演算子とdelete演算子を使用する必要があります。
とにかく、スピードブーストを利用できるのであれば、なぜあなたは(あなたがあなたのmallocedメモリを解放し、newで割り当てられたオブジェクトを削除するなら)あなたが両方を使うべきではないそのreallocがあなたに与えることができるPODの)。
あなたがそれを必要としない限り、あなたはC++でnew/deleteに固執するべきです。
C++を使用している場合は、malloc/callocではなくnew/deleteを使用してみてください。 malloc/callocの場合は、別のヘッダーを含める必要があります。同じコードに2つの異なる言語を混在させないでください。彼らの仕事はあらゆる点で似ており、どちらもハッシュテーブルのヒープセグメントから動的にメモリを割り当てます。
動的割り当ては、オブジェクトの有効期間がそれが作成されるスコープとは異なる場合にのみ必要であり(これはスコープをより小さくするためにも当てはまります)、値でそれを格納しない特別な理由があります作業。
例えば:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
C++ 11以降、割り当てられたメモリを扱うためのstd::unique_ptr
があります。これには割り当てられたメモリの所有権が含まれています。 std::shared_ptr
は、所有権を共有する必要があるときのために作成されました。 (あなたは良いプログラムであなたが期待するよりもこれを必要としないでしょう)
インスタンスを作成するのはとても簡単になります。
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
C++ 17では、std::optional
が追加されているため、メモリ割り当てが不要になります。
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
'instance'が範囲外になるとすぐに、メモリはクリーンアップされます。所有権の譲渡も簡単です。
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.Push_back(std::move(instance)); // std::move -> transfer (most of the time)
それでは、いつnew
がまだ必要ですか。 C++ 11以降ではほとんどありません。生のポインタを介して所有権を譲渡するAPIに到達するまで、ほとんどの人はstd::make_unique
を使用します。
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
C++ 98/03では、手動でメモリ管理をする必要があります。この場合は、より新しいバージョンの標準にアップグレードしてみてください。あなたが動けなくなる場合:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
メモリリークが発生しないように、所有権を正しく追跡してください。 Move Semanticsもまだ機能しません。
それでは、いつC++でmallocが必要になるのでしょうか。唯一の正当な理由は、メモリを割り当て、あとでnewを配置して初期化することです。
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
それでも、上記は有効ですが、これはnew-operatorを介しても実行できます。 std::vector
はその良い例です。
最後に、我々はまだ部屋にゾウがいます:C
。メモリがC++コードで割り当てられ、Cコードで解放される(またはその逆の方法で)Cライブラリを使用する必要がある場合は、malloc/freeを使用する必要があります。
この場合、仮想関数、メンバ関数、クラスなどを忘れてください。PODを持つ構造体だけが許可されます。
規則のいくつかの例外:
C++に移植したいCコードがある場合は、その中にmalloc()呼び出しを残すことができます。新しいC++コードの場合は、代わりにnewを使用することをお勧めします。
new
は、構造体のデフォルト値を初期化し、その中の参照をそれ自身に正しくリンクします。
例えば。
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
そのため、new struct test_s
は作業参照を持つ初期化された構造体を返しますが、mallocされたバージョンはデフォルト値を持たず、内部参照は初期化されません。
低い観点から見ると、newはメモリを提供する前にすべてのメモリを初期化しますが、mallocはメモリの元の内容を保持します。
New/deleteの代わりにmalloc/freeを使用することを検討するのは稀ですが、C++ではreallocに似た機能がないため、reallocを使用して(オブジェクトではなく単純pod型を)再割り当てする場合です。もっとC++のアプローチ)。
new
演算子とdelete
演算子はクラスと構造体で操作できますが、malloc
とfree
はキャストが必要なメモリブロックでのみ機能します。
new/delete
を使用すると、割り当てられたメモリを必要なデータ構造にキャストする必要がなくなるため、コードの改善に役立ちます。