2015-12-15 16 views
5

Ich habe den folgenden Code:Zugang Variable außerhalb try-catch-Block

class ClassA 
{ 
public: 
    ClassA(std::string str); 
    std::string GetSomething(); 
}; 

int main() 
{ 
    std::string s = ""; 
    try 
    { 
     ClassA a = ClassA(s); 
    } 
    catch(...) 
    { 
     //Do something 
     exit(1); 
    } 

    std::string result = a.GetSomething(); 

    //Some large amount of code using 'a' out there. 
} 

Ich würde die letzte Zeile wie könnte die a Variable zugreifen. Wie kann ich das erreichen, wenn ClassA keinen Standardkonstruktor ClassA() hat und ich keine Zeiger verwenden möchte? Ist die einzige Möglichkeit, einen Standardkonstruktor zu ClassA hinzuzufügen?

+3

Warum nicht im 'try' Block? Wenn Sie eine Ausnahme auslösen, werden Sie sowieso "exit". – MatthewRock

+3

Die Frage ergibt keinen Sinn: Wenn die Konstruktion von 'a' eine Ausnahme auslöst, ist' a' sowieso nutzlos. –

+2

'catch' muss Zeug nach sich haben, z.B. 'catch (...)' –

Antwort

9

Sie können nicht oder sollten nicht. Stattdessen könnte man es nur zum Einsatz innerhalb der try Block, so etwas wie:

try 
{ 
    ClassA a = ClassA(s); 

    std::string result = a.GetSomething(); 
} 
catch(...) 
{ 
    //Do something 
    exit(1); 
} 

Der Grund dafür ist, dass seit a nach dem try Block außerhalb des Bereichs geht auf das Objekt verweisen danach nicht definiertes Verhalten ist (wenn Sie einen Zeiger haben zu wo es war).

Wenn Sie mit a.GetSomething oder die Zuordnung throw s betroffen sind können Sie eine try-catch um das setzen:

try 
{ 
    ClassA a = ClassA(s); 

    try { 
     std::string result = a.GetSomething(); 
    } 
    catch(...) { 
     // handle exceptions not from the constructor 
    } 
} 
catch(...) 
{ 
    //Do something only for exception from the constructor 
    exit(1); 
} 
5

Sie können eine Art von optional verwenden oder einfach nur std::unique_ptr verwenden.

int main() 
{ 
    std::string s = ""; 
    std::unique_ptr<ClassA> pa; 
    try 
    { 
     pa.reset(new ClassA(s)); 
    } 
    catch 
    { 
     //Do something 
     exit(1); 
    } 

    ClassA& a = *pa; // safe because of the exit(1) in catch() block 
    std::string result = a.GetSomething(); 

    //Some large amount of code using 'a' out there. 
} 
2

Natürlich nur zur Verlängerung des try Block die Verwendung von a enthalten ist die einfachste Lösung.

Auch wenn Sie wirklich planen, exit(1) oder auf andere Weise das Programm bei einem Fehler abzubrechen, dann stellen Sie einfach keinen try Block hier überhaupt. Die Ausnahme wird sich ausbreiten und das Programm abbrechen, wenn es nicht abgefangen wird.


Eine Alternative ist std::optional zu verwenden. Dies ist die gleiche Art von Konzept wie die Verwendung eines Zeigers, aber es verwendet die automatische Zuweisung, sodass Sie weniger wahrscheinlich ein Speicherleck erstellen. Dies ist derzeit experimentelle Status; Sie können boost::optional stattdessen verwenden, wenn Ihr Compiler nicht std::experimental::optional hat:

#include <experimental/optional> 
using std::experimental::optional; 
using std::experimental::in_place; 

// ... 

    optional<ClassA> a; 

    try 
    { 
     a = optional<ClassA>(in_place, s); 
    } 
    catch(...) 
    { 
     // display message or something 
    } 

    std::string result; 
    if (a) 
     result = a->GetSomething(); 

Ich mag wiederholen würde aber, dass dies ein bisschen ein Spaghetti-Stil ist, und es wäre besser, den Code anders zu gestalten, so dass Sie überprüfen nicht ständig, ob die Konstruktion erfolgreich war oder fehlgeschlagen ist.

Dies erfordert ClassA beweglich oder kopierbar sein. Das in_place ist ein spezielles Argument, das einen perfekten Weiterleitungskonstruktor für die verbleibenden Argumente aufruft. Ohne in_place können Sie nur ein tatsächliches ClassA als Konstruktorargument angeben, es berücksichtigt keine impliziten Konvertierungen zu ClassA. (So ​​vermeidet optional die Mehrdeutigkeit zwischen Kopie-Konstruktion und Listen-Initialisierung von Objekt des gleichen Typs).