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.
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<T, N>
: un tableau fixe de N
éléments de type T
.
std::vector<T>
: un tableau redimensionnable d'éléments de type T
.
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:
new int(42)
g()
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 .
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.
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.
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.
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:
Mesurez vos performances.
Écrivez des implémentations de remplacement pour la fonction globale operator new()
et ses amis.
Mesurez vos performances et comparez.
Les gains/pertes peuvent être importants ou non.