Het vrijmaken van lokale buffers bij het weggooien van uitzonderingen in C ++

Stel dat ik een volgende constructor in de C ++ -klasse heb:

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: ik haat malloc/gratis en nieuw/verwijderen, maar helaas moet ik buffers gebruiken voor het laden van texturen die vervolgens worden doorgegeven aan ID3D10ShaderResourceView, ID3D10Buffer, vertex-buffer en dergelijke. Voor alle daarvan is een buffer nodig.

Wat ik probeer te doen om uitzonderingen te gebruiken in plaats van foutcodes te retourneren. Ook wil ik buffers maken waar ze nodig zijn en ze vrijmaken direct nadat ze niet langer nodig zijn.

Wat er echter lelijk uitziet, is dat in geval van fouten, ongeacht of ik foutcodes retourneer of uitzonderingen gooi, ik nog steeds moet onthouden dat ik elke buffer die tot dat moment is gemaakt, opruimt. Als ik 10 buffers en 10 mogelijke foutpunten heb, moet ik 100 keer gratis bellen() (vergeet niet elke buffer in elke foutcase vrij te maken).

Stel nu dat ik of erger, mijn collega wil een beetje logica wijzigen en, laten we zeggen, ergens in het midden een extra buffer toevoegen. Nu zou hij door alle fouten heen moeten kijken die zich in de rest van de methode kunnen voordoen en op elke plaats gratis() voor die buffer kunnen toevoegen. Als hij haast heeft, kan hij gemakkelijk een paar van dergelijke plaatsen over het hoofd zien, en je hebt een geheugenlek.

Dit maakt de code ook immens.

uiteindelijk sleutelwoord zou dat probleem in Java of C# oplossen. Ongeacht waar in de code de uitzondering plaatsvond, zou ik nog steeds al die buffers in "eindelijk" opruimen (tussen haakjes, dat zou je niet nodig hebben met garbage collection). In C ++ van wat ik begrijp, moet ik mogelijk een ledvariabele maken voor een dergelijke buffer en in de destructor zorgen dat de buffers worden opgeruimd. Ziet er ook heel lelijk uit, omdat een ledvariabele met de naam "pBuffer", zelfs een privé-variabele, gewoon rotzooi is, omdat deze alleen wordt gebruikt in de ene methode (in dit geval constructor) en altijd de NULL is voor de rest van de tijd.

Moet een veel voorkomend probleem zijn, maar het lukte me niet om een ​​antwoord te vinden met behulp van zoeken. Bedankt!

2
Het is waar, maar gebruik liever niet nieuw/verwijderen :) Gebruik containers, slimme aanwijzers en dergelijke om te voorkomen dat u handmatig geheugen moet beheren. Ze maken het veel eenvoudiger om uitzonderingsveiligheid te garanderen.
toegevoegd de auteur Stuart Golodetz, de bron
U moet uit de gewoonte stappen om malloc/free in C ++ te gebruiken, tenzij dit absoluut noodzakelijk is. Gebruik liever nieuw/verwijderen .
toegevoegd de auteur Dave Rager, de bron
@Dave - en als je dat eenmaal hebt gedaan, vermijd nieuw en verwijderen met slimme aanwijzers voor een nog eenvoudiger leven
toegevoegd de auteur Steve Townsend, de bron

6 antwoord

Stop managing memory manually and you won't have these sorts of problems. Use something like std::vector.

Je kunt ook iets als Boost's shared_array gebruiken, maar dat is overdreven voor wat je hier probeert te doen:

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

Het algemene punt dat hier moet worden gemaakt, is dat je het RAII-idioom moet gebruiken - dat wil zeggen dat je bij het verkrijgen van bronnen, ze opslaat in een instantie van een klasse waarvan de destructor ze weer vrijmaakt. Maar als een exemplaar van die resource-owned klasse buiten de scope valt, zijn de resources gegarandeerd vrij.

Kijk hier:

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

8
toegevoegd

Het canonieke antwoord is het principe van "Resource Acquisition Is Initialization" (RAII) en slimme pointers. U maakt een klasse-instantie op de stapel die het geheugen in zijn destructor zal bevrijden, bijvoorbeeld boost :: scoped_ptr .

4
toegevoegd
Bedankt, ik begrijp nu het idee van RAII van de antwoorden van u en anderen))
toegevoegd de auteur iseeall, de bron

Gebruik in plaats daarvan raii :

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

Zoek opnieuw naar 'C ++ slimme aanwijzers'. U hebt een exception-safe wrapper in het geheugen nodig in plaats van raw malloc , die zoals u hebt opgemerkt veel kopzorgen oproept en incidenteel wellicht beter wordt vervangen door operator new nu schrijf je C ++ en geen C-code.

Vorig antwoord hier heeft betrekking op dit gebied.

1
toegevoegd

Het canonieke antwoord hiervoor zal unique_ptr zijn in C ++ 11. Tot die tijd waarschijnlijk scoped_ptr (http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_ptr.htm) voor singletons en scoped_array (http : //www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_array.htm) voor arrays, beide van Boost. Of u kunt het equivalent zelf coderen.

1
toegevoegd

Gebruik hiervoor idiomatische C ++ -aanpak: RAII . Deze Wikipedia-pagina heeft C ++ -voorbeeld.

1
toegevoegd