2010-02-02 33 views
4

Je vois beaucoup de classes d'exemple RAII entourant les handles de fichiers.Utilisation de RAII avec un pointeur de caractères

J'ai essayé d'adapter ces exemples sans chance à un pointeur de caractère.

Une bibliothèque que j'utilise possède des fonctions qui prennent l'adresse d'un pointeur de caractère (déclaré comme get_me_a_string (char ** x)). Ces fonctions allouent de la mémoire pour ce pointeur de caractères et laissent à l'utilisateur final de la bibliothèque le soin de le nettoyer dans son propre code.

Donc, j'ai le code qui ressemble à ceci ...

char* a = NULL; 
char* b = NULL; 
char* c = NULL; 

get_me_a_string(&a); 
if(a == NULL){ 
    return; 
} 


get_me_a_beer(&b); 
if(b == NULL){ 
    if(a != NULL){ 
     free(a); 
    } 
    return; 
} 


get_me_something(&c); 
if(c == NULL){ 
    if(a != NULL){ 
     free(a); 
    } 
    if(b != NULL){ 
     free(b); 
    } 
    return; 
} 

if(a != NULL){ 
    free(a); 
} 
if(b != NULL){ 
    free(b); 
} 
if(a != NULL){ 
    free(b); 
} 

On dirait que RAII est la réponse pour ce gâchis que j'ai ci-dessus. Quelqu'un pourrait-il fournir une simple classe C++ qui enveloppe un char * plutôt qu'un fichier *?

Merci

+0

La plupart des bibliothèques qui allouent la mémoire ont une fonction pour le libérer. (Voir XmStringCreate et XmStringFree). Votre bibliothèque a-t-elle une fonction de désallocation similaire? – Bill

+0

Oui, il a sa propre fonction gratuite, mais c'est à moi de l'appeler. –

Répondre

2

Une implémentation très basique (que vous devriez faire noncopyable etc).

struct CharWrapper { 
    char* str; 
    CharWrapper(): str() {} // Initialize NULL 
    ~CharWrapper() { free(str); } 
    // Conversions to be usable with C functions 
    operator char**() { return &str; } 
    operator char*() { return str; } 
}; 

Ceci est techniquement pas RAII, comme une initialisation se produit au plus tard au constructeur, mais il prendra soin de nettoyage.

+0

Je suis déjà loin. Je ne sais pas comment l'utiliser réellement. Comment déclarer des objets de ce type (est-ce réellement un objet, vous avez utilisé struct). Comment transmettre les objets déclarés à ces fonctions de la bibliothèque? –

+0

CharWrapper str1; get_me_a_string (str1); puts (str1); Les opérateurs de conversion peuvent être quelque peu problématiques, alors envisagez de les remplacer par des fonctions d'accès. La seule différence entre struct et class est la visibilité par défaut. Pour les structures c'est public, pour les classes c'est privé. – Tronic

+0

Je viens de tester cela. Est-il censé être résistant aux segavages? Si c'est le cas, cela ne fonctionne pas car la mémoire n'est pas gratuite. Sinon, cela semble fonctionner correctement. La seule chose que je n'aime pas à ce sujet est qu'en appelant printf, je dois maintenant le lancer en tant que (char *). Appeler d'autres fonctions semble fonctionner sans aucun cast du tout (surchargement C++ au travail?) –

5

Il existe déjà quelque chose dans la bibliothèque standard: il s'appelle std::string.

Edit: À la lumière de nouvelles informations:

Il allouer de la mémoire et le remplir vers le haut. Je pourrais copier le contenu dans un nouvel objet std :: string mais je devrais encore libérer la mémoire allouée par la fonction.

Ceci est une mauvaise conception de la part de l'implémenteur - le module qui alloue devrait être responsable de la désallocation. Ok, maintenant que je l'ai sorti de mon système: vous pouvez utiliser un boost::shared_ptr pour libérer.

template<typename T> 
struct free_functor 
{ 
    void operator() (T* ptr) 
    { 
     free(ptr); 
     ptr=NULL;    
    } 
}; 
shared_ptr<X> px(&x, free_functor()); 
+0

Je pense qu'il est coincé avec une bibliothèque qui retourne des chaînes C qui doivent être libérées. – Tronic

+0

Je ne pense pas que 'auto_ptr' fonctionnera, puisqu'il doit s'agir d'un' free() 'et non d'un' delete'. Je crois que 'boost :: scoped_ptr' vous permettra de spécifier un suppresseur personnalisé, cependant. –

+0

En fait, je pense que 'scoped_ptr' n'autorise pas de suppresseur personnalisé. 'shared_ptr' fait, cependant. –

0

je pense auto_ptr est ce que vous voulez

ou stimuler shared_ptr si la sémantique de auto_ptr ne travaillent pas pour vous

+0

auto_ptr supprime le contenu, mais il a besoin de free(). – Tronic

+0

ah oui - vous pouvez fournir client deleter Mais je vais voter pour votre réponse de toute façon – pm100

+0

auto_ptr ne joue pas aussi bien avec les tableaux – JohnMcG

0

soit utiliser std::string ordinaire ou boost::scoped_array pour les tableaux locaux ou boost::shared_array pour les chaînes partagées (celui-ci vous permet de fournir Deleter personnalisée pour appeler free())

1

vous pouvez essayer quelque chose comme ceci:.

template <typename T> 
class AutoDeleteArray 
{ 
public: 
    explicit AutoDeleteArray(const T* ptr) 
     : ptr_(ptr) 
    {} 
    ~AutoDeleteArray() 
    { 
     delete [] ptr_; 
     // if needed use free instead 
     // free(ptr_); 
    } 

private: 
    T *ptr_; 
}; 

// and then you can use it like: 
{ 
    char* a = NULL; 

    get_me_a_string(&a); 
    if(a == NULL) 
     return; 

    AutoDeleteArray<char> auto_delete_a(a); 
} 

Ce n'est pas la solution la plus fiable, mais pourrait suffire à cette fin. PS: Je me demandais std::tr1::shared_ptr avec le travail de deleter personnalisé aussi bien?

0

Merci à tous pour vos réponses.

Malheureusement, je ne peux pas utiliser boost, ou d'autres bibliothèques sur ce projet ... donc toutes ces suggestions sont inutiles pour moi.

J'ai regardé des choses comme la gestion des exceptions dans C comme ici ... http://www.halfbakery.com/idea/C_20exception_20handling_20macros

Et puis je regardais pourquoi C++ n'a pas finalement comme Java et suis tombé sur ce genre de choses RAII.

Je ne suis toujours pas sûr si je vais aller dans le sens de destructor et rendre le code C++ uniquement, ou le bâton avec de C exception macro (qui utilisent le goto redoutée :)

Tronic a suggéré quelque chose comme ce qui suit. Avec RAII, ou les destructeurs en général, sont-ils censés être à l'épreuve des coupures? Je ne devine pas. La seule chose que je n'aime pas est le fait que je dois maintenant utiliser un cast (char *) dans mes instructions printf.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

struct CharWrapper { 
    char* str; 
    CharWrapper(): str() {} // Initialize NULL 
    ~CharWrapper() { 
     printf("%d auto-freed\n", str); 
     free(str); 
    } 
    // Conversions to be usable with C functions 
    operator char*() { return str; } 
    operator char**() { return &str; } 
}; 

// a crappy library function that relies 
// on the caller to free the memory 
int get_a_str(char **x){ 
    *x = (char*)malloc(80 * sizeof(char)); 
    strcpy(*x, "Hello there!"); 
    printf("%d allocated\n", *x); 
    return 0; 
} 


int main(int argc, char *argv[]){ 
    CharWrapper cw; 
    get_a_str(cw); 
    if(argc > 1 && strcmp(argv[1], "segfault") == 0){ 
     // lets segfault 
     int *bad_ptr = NULL; 
     bad_ptr[8675309] = 8675309; 
    } 
    printf("the string is : '%s'\n", (char*)cw); 
    return 0; 
} 
0

Une solution alternative serait quelque chose comme ça, ce qui est de savoir comment j'écrire ce code dans C:

char* a = NULL; 
char* b = NULL; 
char* c = NULL; 

get_me_a_string(&a); 
if (!a) { 
    goto cleanup; 
} 

get_me_a_beer(&b); 
if (!b) { 
    goto cleanup; 
} 

get_me_something(&c); 
if (!c) { 
    goto cleanup; 
} 

/* ... */ 

cleanup: 
/* free-ing a NULL pointer will not cause any issues 
* (see C89-4.10.3.2 or C99-7.20.3.2) 
* but you can include those checks here as well 
* if you are so inclined */ 
free(a); 
free(b); 
free(c); 
+2

En C++, le problème est que l'exécution ne peut toujours pas atteindre le nettoyage en raison d'exceptions. Si le code utilise des exceptions n'importe où, vous devrez également jeter une poignée de blocs try pour le rendre sûr. – UncleBens

+0

Oui, je discutais sur le faire (quoique par des macros) avec ce ... http://www.halfbakery.com/idea/C_20exception_20handling_20macros UncleBen: cela est en fait juste code C simple en utilisant un compilateur C++. Vistual Studio sous Windows et G ++ sous Linux. –

0

Puisque vous dites que vous ne pouvez pas utiliser boost, il est peu Difficile d'écrire un pointeur intelligent très simple qui ne partage pas ou ne transfère pas de ressources.

Voici quelque chose de basique. Vous pouvez spécifier un foncteur de deleter en tant que paramètre de modèle. Je ne suis pas particulièrement friand des opérateurs de conversion, donc utilisez plutôt la méthode get(). Ajoutez d'autres méthodes comme release() et reset() à volonté.

#include <cstdio> 
#include <cstring> 
#include <cstdlib> 

struct Free_er 
{ 
    void operator()(char* p) const { free(p); } 
}; 

template <class T, class Deleter> 
class UniquePointer 
{ 
    T* ptr; 
    UniquePointer(const UniquePointer&); 
    UniquePointer& operator=(const UniquePointer&); 
public: 
    explicit UniquePointer(T* p = 0): ptr(p) {} 
    ~UniquePointer() { Deleter()(ptr); } 
    T* get() const { return ptr; } 
    T** address() { return &ptr; } //it is risky to give out this, but oh well... 
}; 

void stupid_fun(char** s) 
{ 
    *s = static_cast<char*>(std::malloc(100)); 
} 

int main() 
{ 
    UniquePointer<char, Free_er> my_string; 
    stupid_fun(my_string.address()); 
    std::strcpy(my_string.get(), "Hello world"); 
    std::puts(my_string.get()); 
}