web-dev-qa-db-ja.com

どのような場合にmallocやnewを使用しますか?

私はC++でデータを割り当てて解放する複数の方法があり、mallocを呼び出すときはfreeを呼び出す必要があり、new演算子を使用するときはdeleteとペアにする必要があることを理解しています(たとえば、free()の呼び出しnew演算子を使用して作成されたものについて)、しかし、malloc/freeをいつ使用し、実際の世界のプログラムでnew/deleteを使用する必要があるのか​​は明確ではありません。

あなたがC++の専門家である場合は、この点で従う経験則や慣習を教えてください。

436
Ralph Burgess

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/freemalloc/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
342
Brian R. Bondy

簡単に言えば、C++にmallocを使わないでください。 mallocをC++で使用すると、newを克服するために定義されていた多くの欠点があります。

C++コードの不具合を新規修正

  1. 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
    }
    
  2. それよりも悪いです。問題の型が POD(plain old data) の場合、最初のf2と同じように、mallocを使用してメモリを割り当てることができます。例です。

    型がPODの場合でもそれほど明確ではありません。特定の型がPODから非PODに変更されてもコンパイラエラーが発生せず、問題をデバッグするのが非常に困難であるという事実は重要な要素です。例えば、誰か(おそらく他のプログラマ、メンテナンス中に、fooがPODではなくなるような変更を加えた場合)コンパイル時に明らかなエラーは表示されません。

    struct foo {
      double d[5];
      virtual ~foo() { }
    };
    

    明らかな診断なしに、f2mallocも悪くなるでしょう。ここの例は些細なことですが、誤って(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 であるかどうか判断できないが。

  3. 割り当てに失敗した場合、mallocNULLを返します。 newstd::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;
    }
    
  4. 基本的に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();
    }
    
  5. 私たちの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を使うこともできます。

132
Flexo

C++ FQA Liteから

[16.4]信頼できる古いmalloc()の代わりにnewを使うべきなのはなぜですか?

FAQ:new/deleteはコンストラクタ/デストラクタを呼び出します。 newは型保証されていますが、mallocは安全ではありません。 newはクラスによってオーバーライドすることができます。

FQA:FAQで言及されているnewの美徳は美徳ではありません。コンストラクタ、デストラクタ、および演算子のオーバーロードはゴミです(ガベージコレクションがない場合の動作を参照してください)。ちょっとここで(通常、mallocによって返されたvoid *を正しいポインタ型にキャストして型付けされたポインタ変数に割り当てる必要がありますが、これは面倒ですが、「安全ではない」とは言い難い場合があります)。

ああ、そして信頼できる古いmallocを使うことで、同じくらい信頼できる&古いreallocを使うことが可能になります。残念ながら、私たちは光沢のある新しい演算子を更新するなどしていません。

それでも、言語がC++であっても、newは、言語全体で使用される一般的なスタイルからの逸脱を正当化するのに十分なほど悪くはありません。特に、単純なオブジェクトをmallocしただけでは、自明でないコンストラクタを持つクラスは致命的な動作をします。それでは、なぜコード全体でnewを使用しないのですか?人々がオペレータnewを過負荷にすることはめったにないので、おそらくそれはあなたのやり方ではあまりやり過ぎないでしょう。そして、彼らが新しく過負荷になった場合、あなたはいつでも彼らにやめるように頼むことができます。

すみません、私はただ抵抗できませんでした。 :)

50

C++では常にnewを使用してください。型なしのメモリブロックが必要な場合は、operator newを直接使用できます。

void *p = operator new(size);
   ...
operator delete(p);
48
Ferruccio

Cによって管理されるメモリを割り当てるには、mallocおよびfreeonlyを使用します。中心のライブラリとAPI。あなたがコントロールするものすべてにnewdelete(そして[]の変種)を使ってください。

30
dmckee

new vs malloc()

1)newoperatormalloc()functionです。

2)newconstructorsを呼び出しますが、malloc()は呼び出しません。

3)newexactデータ型を返し、malloc()void *を返します。

4)newNULL(失敗した場合にスローされます)を決して返しませんが、malloc()はNULLを返します。

5)malloc()はできるがnewでは処理されないメモリの再割り当て

24
Yogeesh H T

あなたの質問に答えるために、あなたはmallocnewの違いを知っているべきです。違いは簡単です。

mallocはメモリを割り当てますが、newはメモリを割り当て、メモリを割り当てているオブジェクトのコンストラクタを呼び出します。

ですから、Cに制限されているのでなければ、特にC++オブジェクトを扱うときは、決してmallocを使用しないでください。それはあなたのプログラムを破るためのレシピでしょう。

freedeleteの違いも全く同じです。違いは、deleteはメモリの解放に加えて、あなたのオブジェクトのデストラクタを呼び出すということです。

14

mallocnewには大きな違いがあります。 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
10

newmallocで行わないことがいくつかあります。

  1. newは、そのオブジェクトのコンストラクタを呼び出すことによってオブジェクトを構築します
  2. newは割り当てられたメモリの型キャストを必要としません。
  3. 大量のメモリを割り当てる必要はありません。むしろ、多数のオブジェクトを構築する必要があります。

ですから、もしあなたがmallocを使うのであれば、上記のことを明示的に行う必要があります。さらに、newはオーバーロードすることができますが、mallocはオーバーロードすることはできません。

6
herohuyongtao

あなたが構築/破壊を必要とせず、再割り当てを必要とするデータ(例えば、たくさんのint)を扱うのであれば、malloc/freeがreallocを与えるので良い選択だと思います。 -delete(これは私のLinuxマシンにありますが、これはプラットフォームに依存するかもしれないと思います)。 PODではないC++オブジェクトを使用していて、構築/破棄が必要な場合は、new演算子とdelete演算子を使用する必要があります。

とにかく、スピードブーストを利用できるのであれば、なぜあなたは(あなたがあなたのmallocedメモリを解放し、newで割り当てられたオブジェクトを削除するなら)あなたが両方を使うべきではないそのreallocがあなたに与えることができるPODの)。

あなたがそれを必要としない限り、あなたはC++でnew/deleteに固執するべきです。

4
PSkocik

C++を使用している場合は、malloc/callocではなくnew/deleteを使用してみてください。 malloc/callocの場合は、別のヘッダーを含める必要があります。同じコードに2つの異なる言語を混在させないでください。彼らの仕事はあらゆる点で似ており、どちらもハッシュテーブルのヒープセグメントから動的にメモリを割り当てます。

3
user3488100

動的割り当ては、オブジェクトの有効期間がそれが作成されるスコープとは異なる場合にのみ必要であり(これはスコープをより小さくするためにも当てはまります)、値でそれを格納しない特別な理由があります作業。

例えば:

 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を持つ構造体だけが許可されます。

規則のいくつかの例外:

  • Mallocが適切な、高度なデータ構造を持つ標準ライブラリを書いています。
  • あなたは大量のメモリを割り当てなければなりません(10GBのファイルのメモリコピーに?)
  • 特定の構成要素を使用できないツールがあります
  • 不完全型を保存する必要があります
2
JVApen

C++に移植したいCコードがある場合は、その中にmalloc()呼び出しを残すことができます。新しいC++コードの場合は、代わりにnewを使用することをお勧めします。

2
Fred Larson

newは、構造体のデフォルト値を初期化し、その中の参照をそれ自身に正しくリンクします。

例えば。

struct test_s {
    int some_strange_name = 1;
    int &easy = some_strange_name;
}

そのため、new struct test_sは作業参照を持つ初期化された構造体を返しますが、mallocされたバージョンはデフォルト値を持たず、内部参照は初期化されません。

1
lama12345

低い観点から見ると、newはメモリを提供する前にすべてのメモリを初期化しますが、mallocはメモリの元の内容を保持します。

1
Peiti Li

New/deleteの代わりにmalloc/freeを使用することを検討するのは稀ですが、C++ではreallocに似た機能がないため、reallocを使用して(オブジェクトではなく単純pod型を)再割り当てする場合です。もっとC++のアプローチ)。

0

new演算子とdelete演算子はクラスと構造体で操作できますが、mallocfreeはキャストが必要なメモリブロックでのみ機能します。

new/deleteを使用すると、割り当てられたメモリを必要なデータ構造にキャストする必要がなくなるため、コードの改善に役立ちます。

0
selwyn