2010-08-13 24 views
19

Une partie de mon code utilise toujours malloc au lieu de new. La raison en est que j'ai peur d'utiliser new car il émet une exception, plutôt que de renvoyer NULL, que je peux facilement vérifier. Envelopper chaque appel à new dans un try{}catch(){} n'a pas l'air aussi bien. Alors que lorsque vous utilisez malloc je peux simplement faire if (!new_mem) { /* handle error */ }.Est-il possible d'utiliser des pointeurs intelligents C++ avec malloc?

Par conséquent, j'ai une question. Puis-je utiliser des pointeurs intelligents avec malloc?

Quelque chose comme:

SmartPointer<Type> smarty = malloc(sizeof(Type)); 

Quelque chose comme ça.

Est-ce possible?

Merci, Boda Cydo.

+2

Si vous voulez que new lance maintenant une exception et renvoie simplement NULL, vous pouvez utiliser: Type * bla = new (std :: nothrow) Type() ;. Je pense qu'il est préférable d'utiliser std :: nothrow que malloc puisque le dernier n'appelle pas le constructeur. –

+1

Qu'allez-vous faire dans le code de gestion des erreurs pour compenser l'absence de mémoire? Habituellement, le test est si l'alloc a travaillé alors faire du travail. Si elle a échoué, quittez avec un code d'erreur (qui est un moyen compilé de passer le contrôle de la pile). –

Répondre

31

Si vous utilisez shared_ptr ou unique_ptr, vous pouvez spécifier un Deleter personnalisé. Par exemple,

struct free_delete 
{ 
    void operator()(void* x) { free(x); } 
}; 

Cela peut être utilisé avec shared_ptr comme ceci:

std::shared_ptr<int> sp((int*)malloc(sizeof(int)), free_delete()); 

Si vous utilisez unique_ptr, la Deleter est une partie du type du unique_ptr, de sorte que le Deleter doit être comme argument de modèle:

std::unique_ptr<int, free_delete> up((int*)malloc(sizeof(int))); 

Cependant, il est préférable d'utiliser des exceptions correctement, plutôt que de les éviter, lors de l'écriture C++, en particulier avec r espérer des échecs d'allocation. Dans la plupart des cas, vous ne parvenez pas à vous remettre d'un échec d'allocation dans la fonction qui tente d'effectuer l'allocation. Les exceptions peuvent donc vous aider à gérer l'erreur lorsque vous êtes en mesure de la gérer.

+0

Vous pouvez également passer directement 'free':' auto myPointer = std :: unique_ptr (..., gratuit) ' –

+2

@PatrickChilton Notez que si vous faites cela, votre' unique_ptr' sera deux fois plus grand. –

1

Cela dépend de ce que fait le SmartPointer lors de sa destruction. Si vous pouvez spécifier free comme un deallocator, cela pourrait fonctionner. Par exemple, boost :: shared_ptr vous permet de spécifier un suppresseur.

Je n'ai pas assez prêté attention à votre raison de vouloir cela. Je suis d'accord avec les autres réponses que l'utilisation du nothrow new est une bien meilleure idée.

1

Il est possible d'utiliser malloc avec des pointeurs intelligents (vous devez cependant renvoyer la valeur de retour au type de pointeur cible et fournir un deallocator personnalisé). Mais une meilleure option consiste à utiliser la version nothrow de l'opérateur new.

http://www.cplusplus.com/reference/std/new/nothrow/

9

Vous pouvez utiliser nothrow mot-clé avec le nouvel opérateur, qui retournera NULL plutôt que jeter une exception. Pour plus de détails voir lien ci-dessous: http://www.cplusplus.com/reference/std/new/nothrow/

+1

Est-ce que l'utilisation de 'nothrow' est une pratique courante? (Parce que je n'ai pas vu moi-même personnellement l'utilisation du nothrow dans la pratique réelle. – bodacydo

+7

C'est le moyen le plus courant d'utiliser 'new' quand vous ne voulez pas qu'il lance une exception. Mais non, il est plus courant d'utiliser 'new' et de travailler * avec * la gestion des exceptions. Si l'allocation échoue, vous ne pouvez généralement pas faire grand-chose à ce sujet, donc laisser une propagation se propager est une façon assez simple de quitter normalement. – jalf

1

Quel code entre /* handle error */? Y at-il quelque chose que vous pouvez faire avec une erreur de mémoire insuffisante? Je laisse juste l'application se terminer avec une pile d'appels (core dump) donc j'ai une idée au moins un endroit possible qui pourrait causer des problèmes.L'utilisation de malloc pour allouer de la mémoire pour les classes et les objets C++ n'est pas une bonne idée, car elle ne garantit pas l'appel des constructeurs, vous laissant peut-être des classes non initialisées pouvant même planter si elles ont des méthodes virtuelles.

utiliser juste new et delete et ne vous inquiétez pas attraper l'exception, après tout court de mémoire est un cas exceptionnel et ne devrait pas se produire dans les courses normales de l'application.

+0

Il existe certaines situations dans lesquelles vous pouvez _can_ gérer l'erreur. Par exemple, les calculs numériques grevant la mémoire (ex: simulations) peuvent facilement perdre de la mémoire sur les machines faibles, mais dans ce cas, vous pouvez vous arrêter immédiatement et afficher une erreur au lieu de planter. – Yury

1

Utiliser nothrow.

nothrow constante

Cette valeur constante est utilisée comme argument pour nouvel opérateur opérateur nouveau [] pour indiquer que ces fonctions ne doivent pas jeter une exception en cas d'échec , mais le retour d'un null pointeur à la place.

char* p = new (nothrow) char [1048576]; 
if (p==NULL) cout << "Failed!\n"; 
else { 
    cout << "Success!\n"; 
    delete[] p; 
} 
3

La meilleure solution est d'utiliser new (std::nothrow) Type. Cela va agir comme new Type, mais donnera null plutôt que de lancer si elle échoue. Ce sera beaucoup plus facile que d'essayer de faire malloc se comporter comme new.

Si vous devez vraiment utiliser malloc, alors souvenez-vous de construire et de détruire l'objet correctement:

void* memory = malloc(sizeof(Type)); 
Type* object = new (memory) Type; 
object->~Type(); 
free(object); // or free(memory) 

Vous pouvez l'utiliser avec un des pointeurs intelligents en lui donnant un Deleter personnalisé:

void malloc_deleter(Type* object) 
{ 
    object->~Type(); 
    free(object); 
} 

if (void* memory = malloc(sizeof(Type))) 
{ 
    Type* object = new (memory) Type; 
    std::shared_ptr<Type> ptr(object, malloc_deleter); 
    DoStuff(ptr); 
} 

Mais ce serait beaucoup plus simple en utilisant non lanceuse nouveau:

if (Type* object = new (std::nothrow) Type) 
{   
    std::shared_ptr<Type> ptr(object); 
    DoStuff(ptr); 
} 
+0

L'utilisation de malloc a l'avantage supplémentaire de vous permettre d'utiliser realloc: http://stackoverflow.com/a/33706568/1858225 –

1

J'ai une question. Que se passe-t-il si "Type" est un type dont le constructeur peut lancer? Dans ce cas, il faut toujours gérer les exceptions dans un bloc try/catch.

Alors est-ce une bonne idée d'abandonner l'approche basée sur les exceptions? Je dirais que l'on peut utiliser le modèle de conception Usine Usine/Usine Abstraite et avoir tous les nouveaux dans un ensemble relativement moindre de fichiers/espaces de noms/classes, plutôt que ceux-ci étant dispersés tout autour de l'endroit. Cela peut également aider à restreindre l'utilisation du bloc try/catch à un code relativement moindre.