web-dev-qa-db-fra.com

Dans quels cas dois-je utiliser malloc et / ou new?

Je vois qu'en C++, il existe plusieurs façons d'allouer et de libérer des données et je comprends que lorsque vous appelez malloc, vous devez appeler free et lorsque vous utilisez l'opérateur new, vous devez vous associer à delete et c'est une erreur de mélanger les deux (par exemple, Appeler free() sur quelque chose qui a été créé avec l'opérateur new, mais je ne suis pas sûr du moment d'utiliser malloc/free et quand je devrais utiliser new/delete dans mes programmes réels.

Si vous êtes un expert en C++, indiquez-moi les règles de base ou les conventions que vous suivez à cet égard.

436
Ralph Burgess

Sauf si vous êtes obligé d'utiliser C, vous devriez ne jamais utilisermalloc. Toujours utiliser new.

Si vous avez besoin d’une grande quantité de données, procédez comme suit:

char *pBuffer = new char[1024];

Attention, ce n'est pas correct:

//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;

Au lieu de cela, vous devriez le faire lors de la suppression d'un tableau de données:

//This deletes all items in the array
delete[] pBuffer;

Le mot clé new est le moyen de le faire en C++, et il garantira que votre type aura son constructeur appelé. Le mot clé new est également plus sécurité du type alors que malloc n'est pas du tout sûr.

La seule façon dont je pourrais penser qu'il serait avantageux d'utiliser malloc serait si vous aviez besoin de changer la taille de votre tampon de données. Le mot clé new n'a pas de manière analogue à realloc. La fonction realloc pourrait peut-être étendre plus efficacement la taille d’une partie de la mémoire.

Il est à noter que vous ne pouvez pas mélanger new/free et malloc/delete.

Remarque: Certaines réponses à cette question ne sont pas valides.

int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
int* p_array  = new int[5];  // Creates 5 elements
347
Brian R. Bondy

La réponse courte est: n'utilisez pas malloc pour C++ sans une très bonne raison de le faire. malloc présente un certain nombre d'inconvénients lorsqu'il est utilisé avec C++, lequel new a été défini pour être résolu.

Lacunes corrigées par new pour le code C++

  1. malloc n'est pas typé de manière significative. En C++, vous devez convertir le résultat de void*. Cela pose potentiellement beaucoup de problèmes:

    #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. C'est pire que ça cependant. Si le type en question est POD (plain old data) , vous pouvez utiliser de manière semi-sensée malloc pour lui allouer de la mémoire, comme le fait f2 dans le premier exemple.

    Ce n'est pas si évident cependant si un type est POD. Le fait qu'il soit possible pour un type donné de passer d'un POD à un autre, sans qu'il en résulte une erreur du compilateur et un problème potentiellement très difficile à résoudre, est un facteur important. Par exemple, si quelqu'un (peut-être un autre programmeur, beaucoup plus tard lors de la maintenance, apportait une modification qui empêchait foo de devenir POD, aucune erreur évidente ne se produirait au moment de la compilation, comme vous le souhaitiez, par exemple:

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

    rendrait la malloc de f2 également mauvaise, sans aucun diagnostic évident. L'exemple présenté ici est trivial, mais il est possible d'introduire accidentellement un non-PODness beaucoup plus loin (par exemple, dans une classe de base, en ajoutant un membre non-POD). Si vous avez C++ 11/boost, vous pouvez utiliser is_pod pour vérifier que cette hypothèse est correcte et produire une erreur si ce n'est pas le cas:

    #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)));
    }
    

    Bien que boost soit incapable de déterminer si un type est POD sans C++ 11 ou d'autres extensions du compilateur.

  3. malloc renvoie NULL si l'attribution échoue. new lancera std::bad_alloc. Le comportement de l'utilisation ultérieure d'un pointeur NULL n'est pas défini. Une exception a une sémantique propre lorsqu'elle est renvoyée et elle est renvoyée de la source de l'erreur. Envelopper malloc avec un test approprié à chaque appel semble fastidieux et sujet aux erreurs. (Il suffit d'oublier une fois pour annuler tout ce bon travail). Une exception peut être autorisée à se propager à un niveau où un appelant est capable de la traiter de manière judicieuse, où NULL est beaucoup plus difficile à renvoyer de manière significative. Nous pourrions étendre notre fonction safe_foo_malloc pour générer une exception, quitter le programme ou appeler un gestionnaire:

    #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. Fondamentalement malloc est une fonctionnalité C et new est une fonctionnalité C++. En conséquence, malloc ne fonctionne pas correctement avec les constructeurs. Il se contente d’allouer un bloc d’octets. Nous pourrions étendre notre safe_foo_malloc pour continuer à utiliser le placement new:

    #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. Notre fonction safe_foo_malloc n'est pas très générique - idéalement, nous voudrions quelque chose qui puisse gérer tout type, pas seulement foo. Nous pouvons y parvenir avec des modèles et des modèles variadiques pour les constructeurs autres que ceux par défaut:

    #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)...);
      }
    };
    

    Maintenant, nous avons pratiquement réinventé l’opérateur par défaut new en résolvant tous les problèmes que nous avions identifiés jusqu’à présent. Si vous utilisez malloc et le placement new, vous pouvez aussi bien utiliser new pour commencer!

134
Flexo

De la C++ FQA Lite :

[16.4] Pourquoi devrais-je utiliser un nouveau malloc () digne de confiance?

FAQ: new/delete appelle le constructeur/destructeur; new est de type sûr, pas de malloc; new peut être remplacé par une classe.

FQA: Les vertus de new mentionnées par la FAQ ne sont pas des vertus, car les constructeurs, les destructeurs et la surcharge d'opérateurs sont sans intérêt (voyez ce qui se passe lorsque vous n'avez pas de récupération de place?) minuscule ici (normalement, vous devez convertir le vide * renvoyé par malloc vers le type de pointeur correct pour l'attribuer à une variable pointée typée, ce qui peut être ennuyeux, mais loin d'être "dangereux").

Oh, et utiliser old malloc digne de confiance permet d’utiliser le realloc tout aussi digne de confiance. Dommage que nous n’ayons pas de nouvel opérateur brillant à renouveler ou quelque chose du genre.

Quoi qu'il en soit, le nouveau n'est pas suffisant pour justifier un écart par rapport au style commun utilisé dans un langage, même lorsque le langage est C++. En particulier, les classes avec des constructeurs non-triviaux se comporteront de manière fatale si vous simplement mallociez les objets. Alors pourquoi ne pas utiliser new dans tout le code? Les gens surchargent rarement les nouveaux opérateurs, donc ils ne vous gêneront probablement pas trop. Et s’ils surchargent de nouveau, vous pouvez toujours leur demander de s’arrêter.

Désolé, je n'ai pas pu résister. :)

51
Matthias Benkard

Toujours utiliser new en C++. Si vous avez besoin d'un bloc de mémoire non typé, vous pouvez utiliser directement l'opérateur new:

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

Utilisez malloc et free uniquement pour allouer de la mémoire qui sera gérée par c-centric bibliothèques et API. Utilisez new et delete (et le [] variantes) pour tout ce que vous contrôlez.

30
dmckee

new vs malloc ()

1) new est un opérateur, alors que malloc() est un fonction.

2) new appelle constructeurs, alors que malloc() ne le fait pas.

3) new renvoie type de données exact, alors que malloc() renvoie void *.

4) new ne retourne jamais un NULL (lancera en cas d'échec) tandis que malloc() retournera NULL

5) Réallocation de mémoire non gérée par new alors que malloc() peut

25
Yogeesh H T

Pour répondre à votre question, vous devez savoir la différence entre malloc et new. La différence est simple:

mallocalloue de la mémoire, tandis que newalloue de la mémoire ET appelle le constructeur de l'objet pour lequel vous allouez de la mémoire.

Donc, à moins que vous ne soyez limité au C, vous ne devriez jamais utiliser malloc, en particulier lorsqu'il s'agit d'objets C++. Ce serait une recette pour briser votre programme.

De plus, la différence entre free et delete est pratiquement la même. La différence est que delete appellera le destructeur de votre objet en plus de libérer de la mémoire.

15

Il y a une grande différence entre malloc et new. malloc alloue de la mémoire. C'est bien pour C, car dans C, une masse de mémoire est un objet.

En C++, si vous n'avez pas affaire à des types POD (semblables aux types C), vous devez appeler un constructeur sur un emplacement mémoire pour y placer un objet. Les types non-POD sont très courants en C++, car de nombreuses fonctionnalités C++ rendent automatiquement un objet non-POD.

new alloue de la mémoire et crée un objet sur cet emplacement de la mémoire. Pour les types non-POD, cela signifie qu’il faut appeler un constructeur.

Si vous faites quelque chose comme ça:

non_pod_type* p = (non_pod_type*) malloc(sizeof *p);

Le pointeur que vous obtenez ne peut pas être déréférencé car il ne pointe pas vers un objet. Vous devez appeler un constructeur dessus avant de pouvoir l'utiliser (et cela se fait en utilisant le placement new).

Si, par contre, vous faites:

non_pod_type* p = new non_pod_type();

Vous obtenez un pointeur qui est toujours valide, car new a créé un objet.

Même pour les types de POD, il existe une différence significative entre les deux:

pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;

Ce morceau de code afficherait une valeur non spécifiée, car les objets POD créés par malloc ne sont pas initialisés.

Avec new, vous pouvez spécifier un constructeur à appeler et ainsi obtenir une valeur bien définie.

pod_type* p = new pod_type();
std::cout << p->foo; // prints 0

Si vous le souhaitez vraiment, vous pouvez utiliser new pour obtenir des objets POD non initialisés. Voir cette autre réponse pour plus d'informations à ce sujet.

Une autre différence est le comportement en cas d'échec. Lorsqu'il n'alloue pas de mémoire, malloc renvoie un pointeur nul, tandis que new lève une exception.

Dans le premier cas, vous devez tester chaque pointeur renvoyé avant de l’utiliser, tandis que le dernier produira toujours des pointeurs valides.

Pour ces raisons, dans le code C++, vous devez utiliser new et non pas malloc. Mais même dans ce cas, vous ne devriez pas utiliser new "à l’air libre", car il acquiert les ressources que vous devez libérer ultérieurement. Lorsque vous utilisez new, vous devez immédiatement transmettre son résultat à une classe de gestion de ressources:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
10

new fait que malloc ne fait pas:

  1. new construit l'objet en appelant le constructeur de cet objet
  2. new ne nécessite pas de conversion de type de la mémoire allouée.
  3. Il n’est pas nécessaire d’allouer une quantité de mémoire, mais un certain nombre d’objets à construire.

Donc, si vous utilisez malloc, vous devez faire les choses ci-dessus explicitement, ce qui n’est pas toujours pratique. De plus, new peut être surchargé mais malloc ne peut pas l'être.

6
herohuyongtao

Si vous travaillez avec des données qui ne nécessitent pas de construction/destruction et qui nécessitent des réaffectations (par exemple, un grand nombre d’ints), alors je pense que malloc/free est un bon choix car il vous donne realloc, ce qui est bien plus rapide que new-memcpy. -delete (c'est sur ma machine Linux, mais je suppose que cela dépend de la plate-forme). Si vous travaillez avec des objets C++ qui ne sont pas POD et nécessitent une construction/destruction, vous devez utiliser les opérateurs new et delete.

Quoi qu'il en soit, je ne vois pas pourquoi vous ne devriez pas utiliser les deux (à condition que vous libériez votre mémoire mallocée et que vous supprimiez les objets alloués avec new) si vous pouvez tirer parti de l'accélération de la vitesse (parfois importante si vous réaffectez des tableaux de grande taille de POD) que realloc peut vous donner.

Sauf si vous en avez besoin, vous devriez vous en tenir à nouveau/supprimer en C++.

4
PSkocik

Si vous utilisez C++, essayez d'utiliser new/delete au lieu de malloc/calloc car ils sont des opérateurs. Pour malloc/calloc, vous devez inclure un autre en-tête. Ne mélangez pas deux langues différentes dans le même code. Leur travail est semblable à tous égards, les deux allouent de la mémoire dynamiquement à partir du segment de tas dans la table de hachage.

3
user3488100

L'allocation dynamique n'est requise que lorsque la durée de vie de l'objet doit être différente de la portée dans laquelle il est créé (cela s'applique également à la réduction de la portée plus grande) et vous avez une raison spécifique pour laquelle le stockage par valeur ne le permet pas. travail.

Par exemple:

 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);

À partir de C++ 11, nous avons std::unique_ptr pour traiter la mémoire allouée, qui contient la propriété de la mémoire allouée. std::shared_ptr a été créé pour le partage de la propriété. (vous aurez besoin de moins que ce que vous attendez dans un bon programme)

Créer une instance devient vraiment facile:

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 ajoute également std::optional, ce qui peut vous éviter de demander des allocations de mémoire.

auto optInstance = std::optional<Class>{};
if (condition)
    optInstance = Class{};

Dès que "l'instance" est hors de portée, la mémoire est nettoyée. Le transfert de propriété est également facile:

 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)

Alors, quand avez-vous encore besoin de new? Presque jamais à partir de C++ 11. La plupart d’entre vous utilisez std::make_unique jusqu’à atteindre une API qui transfère la propriété via des pointeurs bruts.

 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

En C++ 98/03, vous devez gérer manuellement la mémoire. Si vous êtes dans ce cas, essayez de mettre à niveau vers une version plus récente de la norme. Si vous êtes bloqué:

 auto instance = new Class(); // Allocate memory
 delete instance;             // Deallocate
 auto instances = new Class[42](); // Allocate memory
 delete[] instances;               // Deallocate

Assurez-vous de suivre correctement le propriétaire pour ne pas avoir de fuite de mémoire! La sémantique de déplacement ne fonctionne pas encore non plus.

Alors, quand avons-nous besoin de malloc en C++? La seule raison valable serait d'allouer de la mémoire et de l'initialiser ultérieurement via un nouveau placement.

 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

Même si ce qui précède est valable, vous pouvez également le faire via un nouvel opérateur. std::vector en est un bon exemple.

Enfin, nous avons toujours l’éléphant dans la pièce: C. Si vous devez travailler avec une bibliothèque C où la mémoire est allouée dans le code C++ et libérée dans le code C (ou inversement), vous devez utiliser malloc/free.

Si vous êtes dans ce cas, oubliez les fonctions virtuelles, les fonctions membres, les classes ... Seules les structures contenant des POD sont autorisées.

Quelques exceptions aux règles:

  • Vous écrivez une bibliothèque standard avec des structures de données avancées où malloc est approprié
  • Vous devez allouer de grandes quantités de mémoire (copie en mémoire d’un fichier de 10 Go?)
  • Vous avez un outillage vous empêchant d'utiliser certaines constructions
  • Vous devez stocker un type incomplet
3
JVApen

Si vous souhaitez transférer du code C en C++, vous pouvez y laisser des appels à malloc (). Pour tout nouveau code C++, je vous recommanderais d'utiliser new à la place.

3
Fred Larson

new initialisera les valeurs par défaut de la structure et liera correctement les références qu'il contient à elle-même.

Par exemple.

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

Donc, new struct test_s renverra une structure initialisée avec une référence de travail, alors que la version mallocée n'a pas de valeur par défaut et que les références internes ne sont pas initialisées.

1
lama12345

Dans une perspective inférieure, new initialisera toute la mémoire avant de la donner, tandis que malloc conservera le contenu original de la mémoire.

1
Peiti Li

Il est rare de penser à utiliser malloc/free au lieu de new/delete lorsque vous allouez puis réaffectez (types de pod simples, pas d'objets) à l'aide de realloc car il n'y a pas de fonction similaire à realloc en C++ (bien que cela puisse être fait avec approche plus C++).

0
Florentino Tuason

Les opérateurs new et delete peuvent opérer sur des classes et des structures, alors que malloc et free ne fonctionnent qu'avec des blocs de mémoire devant être convertis.

L'utilisation de new/delete aidera à améliorer votre code car vous n'aurez pas besoin de convertir la mémoire allouée dans la structure de données requise.

0
selwyn