2 votes

Libérer les tampons locaux lors de la levée d'exceptions en C++

Supposons que j'aie le constructeur suivant dans une classe C++ :

MyClass::MyClass()
{
    char* buffer = malloc(100);
    if (0 != someD3DXCallThatCanFail(..., buffer, ...))
    {
        free(buffer);
        throw MyException(L"some message");
    }
    char* buffer2 = malloc(200);
    if (0 != anotherD3DCallThatCanFail(..., buffer2, ...))
    {
        free(buffer);
        free(buffer2);
        throw MyException(L"another message");
    }
    .. more code here where buffer and buffer2 are still used

    free(buffer);
    free(buffer2);
}

EDIT : Je déteste malloc/free et new/delete, mais malheureusement j'ai besoin d'utiliser des buffers pour charger des textures qui sont ensuite transmises à ID3D10ShaderResourceView, ID3D10Buffer, vertex buffer et autres. Tous ces éléments nécessitent des pointeurs vers un tampon.

Ce que j'essaie de faire, c'est d'utiliser des exceptions au lieu de renvoyer des codes d'erreur. Je souhaite également créer des tampons là où ils sont nécessaires et les libérer dès qu'ils ne sont plus nécessaires.

Cependant, ce qui est moche, c'est qu'en cas d'erreur, que je renvoie des codes d'erreur ou que je lève des exceptions, je ne dois pas oublier de nettoyer le tampon qui a été créé jusqu'à ce moment-là. Si j'ai 10 tampons et 10 points d'erreur possibles, je devrai appeler free() 100 fois (dans chaque cas d'erreur, n'oubliez pas de libérer chaque tampon).

Supposons maintenant que je veuille, ou pire, que mon collègue veuille modifier la logique et, par exemple, ajouter un autre tampon quelque part au milieu. Il devra alors examiner toutes les erreurs qui peuvent se produire dans le reste de la méthode et ajouter free() pour ce tampon à chacun de ces endroits. S'il est pressé, il peut facilement négliger quelques endroits de ce type, et vous avez une fuite de mémoire.

Le code s'en trouve considérablement alourdi.

Enfin, un mot-clé résoudrait ce problème en Java ou en C#. Quel que soit l'endroit du code où l'exception s'est produite, je nettoierais toujours tous ces tampons dans "finally" (entre parenthèses, vous n'en auriez pas besoin avec le garbage collection). En C++, d'après ce que j'ai compris, je devrais peut-être créer une variable membre pour chacun de ces tampons, et dans le destructeur, m'assurer que les tampons sont nettoyés. Cela me semble également assez laid, car une variable membre avec le nom "pBuffer", même privée, n'est qu'un déchet, puisqu'elle n'est utilisée que dans une seule méthode (dans ce cas, le constructeur) et qu'elle sera toujours NULL le reste du temps.

Il doit s'agir d'un problème courant, mais je n'ai pas réussi à trouver de réponse à l'aide de la recherche. Je vous remercie de votre attention.

8voto

Stuart Golodetz Points 12679

Arrêtez de gérer la mémoire manuellement et vous n'aurez plus ce genre de problèmes. Utilisez quelque chose comme std::vector<char> .

Vous pouvez également utiliser quelque chose comme la fonction shared_array mais c'est trop pour ce que vous essayez de faire ici :

http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_array.htm

De manière plus générale, il convient d'utiliser l'idiome RAII, c'est-à-dire que lorsque vous acquérez des ressources, vous les stockez dans une instance d'une classe dont le destructeur les libère à nouveau. Ensuite, même si une instance de la classe propriétaire des ressources sort du champ d'application, les ressources sont garanties d'être libérées.

Voir ici :

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

4voto

thiton Points 21303

La réponse canonique est le principe "Resource Acquisition Is Initialization" (RAII) et les pointeurs intelligents. Vous créez une instance de classe sur la pile qui libérera la mémoire dans son destructeur, par exemple boost::scoped_ptr .

2voto

BЈовић Points 28674

Au lieu de cela, utilisez raii :

MyClass::MyClass()
{
    std::vector<char> buffer(100);
    if (0 != someD3DXCallThatCanFail(...))
    {
        throw MyException(L"some message");
    }
    std::vector<char> buffer2c(200);
    if (0 != anotherD3DCallThatCanFail(...))
    {
        throw MyException(L"another message");
    }
    .. more code here 
}

1voto

Victor Sorokin Points 7429

Utiliser l'approche idiomatique du C++ pour cela : RAII . Cette page de Wikipedia contient un exemple de C++.

1voto

Steve Townsend Points 36948

Cherchez à nouveau "pointeurs intelligents C++". Vous avez besoin d'une enveloppe à l'abri des exceptions sur la mémoire au lieu d'une enveloppe brute. malloc qui, comme vous l'avez noté, est source de nombreux maux de tête, et pourrait d'ailleurs être remplacé par operator new vous écrivez maintenant du code C++ et non du code C.

Réponse précédente aquí couvre cette zone.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X