web-dev-qa-db-fra.com

Malloc vs Nouveau pour les primitifs

Je comprends les avantages de l'utilisation de new contre malloc en C++. Mais pour des cas spécifiques tels que les types de données primitifs (non tableau) - int, float etc., est-il plus rapide d'utiliser malloc que new?

Bien qu'il soit toujours conseillé d'utiliser new même pour les primitives, si nous allouons un tableau pour pouvoir utiliser delete[].

Mais pour l'allocation non-tableau, je pense qu'il n'y aurait pas d'appel constructeur pour int? Depuis, l'opérateur new alloue de la mémoire, vérifie si elle est allouée puis appelle le constructeur. Mais pour les allocations de tas non matricielles primitives, est-il préférable d'utiliser malloc que new?

S'il vous plaît donnez votre avis.

25
Rajeev Mehta

N'utilisez jamais malloc en C++. N'utilisez jamais new sauf si vous implémentez une primitive de gestion de mémoire de bas niveau.

La recommandation est:

  • Demandez-vous: "ai-je besoin d'une allocation dynamique de mémoire?". Souvent, vous n'en aurez peut-être pas besoin - préférez les valeurs aux pointeurs et essayez d'utiliser la pile.

  • Si vous avez besoin d'une allocation dynamique de mémoire, demandez-vous "à qui appartiendra la mémoire/l'objet alloué?".

    • Si vous n'avez besoin que d'un seul propriétaire (ce qui est très probable), vous devez utiliser std::unique_ptr . Il s'agit d'une abstraction à coût nul sur new/delete. (Un désallocateur différent peut être spécifié.)

    • Si vous avez besoin d'une propriété partagée, vous devez utiliser std::shared_ptr . Ce n'est pas une abstraction à coût nul, car elle utilise des opérations atomiques et un "bloc de contrôle" supplémentaire pour garder une trace de tous les propriétaires.


Si vous traitez des tableaux en particulier, la bibliothèque standard fournit deux abstractions puissantes et sûres qui ne nécessitent aucune gestion manuelle de la mémoire:

std::array Et std::vector Devraient couvrir 99% de vos "besoins en baie".


Encore une chose importante: la bibliothèque standard fournit les std::make_unique et std::make_shared qui devraient toujours être utilisé pour créer des instances de pointeur intelligent. Il y a quelques bonnes raisons:

  • Plus court - pas besoin de répéter le T (par exemple std::unique_ptr<T>{new T}), pas besoin d'utiliser new.

  • Plus d'exception en toute sécurité. Ils empêchent une fuite de mémoire potentielle causée par l'absence d'un ordre d'évaluation bien défini dans les appels de fonction. Par exemple.

    f(std::shared_ptr<int>(new int(42)), g())
    

    Pourrait être évalué dans cet ordre:

    1. new int(42)
    2. g()
    3. ...

    Si g() lance, le int est divulgué.

  • Plus efficace (en termes de vitesse d'exécution). Cela ne s'applique qu'à std::make_shared - son utilisation au lieu de std::shared_ptr Permet directement à l'implémentation d'effectuer une seule allocation à la fois pour l'objet et pour le bloc de contrôle.

Vous pouvez trouver plus d'informations dans cette question .

84
Vittorio Romeo

Il peut toujours être nécessaire d'utiliser malloc et free en C++ lorsque vous interagissez avec des API spécifiées à l'aide de C simple, car ce n'est pas garanti sans danger pour utiliser free pour désallouer la mémoire allouée avec operator new (qui est finalement ce que toutes les classes de mémoire gérée utilisent), ni d'utiliser operator delete pour désallouer la mémoire allouée avec malloc.

Un exemple typique est POSIX getline (à ne pas confondre avec std::getline ): il faut un pointeur vers un char * variable; cette variable doit pointer vers un bloc de mémoire alloué avec malloc (ou il peut être NULL, auquel cas getline appellera malloc pour vous); lorsque vous avez terminé d'appeler getline, vous devez appeler free sur cette variable.

De même, si vous écrivez une bibliothèque, il peut être judicieux d'utiliser C++ en interne mais de définir un extern "C" API pour vos appelants externes, car cela vous donne une meilleure stabilité de l'interface binaire et une interopérabilité entre les langues. Et si vous renvoyez des objets POD alloués en tas à vos appelants, vous souhaiterez peut-être les laisser désallouer ces objets avec free; ils ne peuvent pas nécessairement utiliser delete, et les faire appeler YourLibraryFree quand aucune opération de type destructeur n'est nécessaire n'est pas ergonomique.

Il peut également être nécessaire d'utiliser malloc lorsque implémente des objets conteneurs redimensionnables, car il n'y a pas d'équivalent de realloc pour operator new.

Mais comme le disent les autres réponses, lorsque vous n'avez pas ce type de contrainte d'interface entre les mains, utilisez plutôt l'une des classes de mémoire gérée.

26
zwol

Il est toujours préférable d'utiliser new. Si vous utilisez malloc, vous devez toujours vérifier manuellement si l'espace est alloué.

Dans le c ++ moderne, vous pouvez utiliser des pointeurs intelligents. Avec make_unique et make_shared vous n'appelez jamais new explicitement. std::unique_ptr n'est pas plus grand que le pointeur sous-jacent et la surcharge d'utilisation est minime.

8
Petar Velev

La réponse à "dois-je utiliser new ou malloc" est règle de responsabilité unique.

La gestion des ressources doit être effectuée par un type qui a cet objectif unique.
Ces classes existent déjà, telles que unique_ptr, vector etc.

Utiliser directement malloc ou new est un péché cardinal.

6
sp2danny

la réponse de zwol donne déjà la bonne réponse: utilisez malloc()/free() lorsque vous interagissez avec les interfaces C uniquement .
Je ne vais pas répéter ces détails, je vais répondre à la question des performances.

La vérité est que les performances de malloc() et new peuvent et diffèrent. Lorsque vous effectuez une allocation avec new, la mémoire sera généralement allouée via un appel à la fonction globale operator new(), qui est distincte de malloc(). Il est trivial d'implémenter operator new() en appelant à malloc(), mais cela n'est pas nécessairement fait.

En fait, j'ai vu un système où une operator new() qui appelle à malloc() dépasserait l'implémentation standard de operator new() d'environ 100 cycles CPU par appel. C'est certainement une différence mesurable et une indication claire que l'implémentation standard fait quelque chose de très différent de malloc().

Donc, si vous êtes préoccupé par les performances, il y a trois choses à faire:

  1. Mesurez vos performances.

  2. Écrivez des implémentations de remplacement pour la fonction globale operator new() et ses amis.

  3. Mesurez vos performances et comparez.

Les gains/pertes peuvent être importants ou non.