2016-06-13 25 views
2

Ich lese, dass intelligente Zeiger hilfreich in Situation, wenn Konstruktor einige Ausnahmen generieren.intelligente Zeiger und Konstruktor Ausnahmen

Das Problem ist, dass Konstruktor einige Ressourcen vor der Generierung von Ausnahmen hat, aber Destruktor nicht aufgerufen wird (und Ressourcen permanent beschäftigt).

Aber ich kann es nicht richtig verstehen. Mein Code:

#include <memory> 
#include <iostream> 

class resOwner { 
    public: 
    resOwner() { 
     std::cout << "Map some huge resources\n"; 
     throw "hi"; 
    } 

    ~resOwner() { 
     std::cout << "Free some huge resources\n"; 
    } 
}; 

class normal : resOwner { 
}; 

int main(){ 
    try { 
     std::shared_ptr<resOwner> k (new resOwner()); 
    } catch (...) {} 
} 

Ausgang ist Map some huge resources. Wie man dieses Ressourcenleck mit intelligenten Zeigern löst?

+1

Keine Sorge - Speicher freigegeben wird, wenn c-tor wirft - ohne Kooperation von Smart-Pointer - siehe http://stackoverflow.com/questions/1674980/who-deletes-the- memory-allowed-within-a-new-operation-which-hat-exception-in – PiotrNycz

+0

Welche Ressource resOwner auch besitzt, muss eine Art Smart-Pointer 'unique_ptr' oder' shared_ptr' usw. sein. Andernfalls werden Sie mit einem Ressourcenleck enden Was immer Sie im Destruktor aufräumen wollten, wird nicht aufgerufen. – Arunmu

+1

Es ist erwähnenswert, dass, wenn ein einzelner Konstruktor vollständig ausgeführt wird (Konstruktordelegierung) und der delegierende Konstruktor einen Writ auslöst, der Destruktor des Objekts aufgerufen wird. – Blazo

Antwort

4

Wie dieses Ressourcenleck mit intelligenten Zeigern lösen?

Die Ressource in einen RAII-Container einbinden und den Container als Mitglied von resOwner speichern. Sie benötigen nicht unbedingt intelligente Zeiger.

struct resource { 
    resource() { 
     std::cout << "Map some huge resources\n"; 
    } 
    ~resource() { 
     std::cout << "Free some huge resources\n"; 
    } 
}; 

struct resOwner { 
    resource res; 
    resOwner() { 
     throw "hi"; 
    } 
}; 

Wenn die riesige Ressource ein dynamisch zugewiesenen Objekt ist, dann müssen Sie keine separate resource Klasse zu implementieren, da die Standard-Bibliothek bereits einen Container für diejenigen hat: std::unique_ptr. Wenn es sich um ein dynamisch zugewiesenes Array handelt, können Sie std::vector verwenden.

Die Funktionsweise: Wenn der Konstruktorhauptteil oder einer der Unterobjektkonstruktoren ausgelöst wird, werden alle Unterobjekte (Mitglieder und Basisobjekte), die bereits erstellt wurden, zerstört. Dies garantiert, dass ~resource aufgerufen wird.

3

Sie sollten intelligente Zeiger mit Ressourcen verwenden, die Ihnen helfen, Ressourcenlecks zu vermeiden. Wie zum Beispiel:

class resource { 
public: 
    resource() { 
     std::cout << "resource allocated\n"; 
    } 
    ~resource() { 
     std::cout << "resource deallocated\n"; 
    } 
}; 

class resOwner { 
    std::shared_ptr<resource> res; 
public: 
    resOwner() : res(new resource) { 
     std::cout << "Map some huge resources\n"; 
     throw "hi"; 
     // res will be destroyed even though exception happened 
    } 
    ~resOwner() { 
     // nothing need to do here 
    } 
}; 

LIVE

1

Die Ausnahme tatsächlich, bevor das Objekt geworfen wird, erzeugt wird. Da kein Objekt erstellt wird, wird sein Destruktor nicht aufgerufen.

Wenn jedoch eine Ausnahme im Konstruktor ausgelöst wird, wird der Destruktor für alle Unterobjekte aufgerufen, von denen der Konstruktor die Ausführung abgeschlossen hat. Versuchen Sie dies zum Vergleich:

class Resource 
{ 
public: 
    Resource() 
    { 
     std::cout << "constructing resource" << std::endl; 
    } 
    ~Resource() 
    { 
     std::cout << "destroying resource" << std::endl; 
    } 
}; 

class Owner 
{ 
    ::std::unique_ptr <Resource> theResource; 
public: 
    Owner() 
      : theResource(new Resource()) 
    { 
     std::cout << "Map some huge resources\n"; 
     throw "hi"; 
    } 

    ~Owner() 
    { 
     std::cout << "Free some huge resources\n"; 
    } 
};