C++ Konstruktoren und Fehlerbehandlung

C

Crymes

Guest
Ich habe folgende Klassen:

Code:
class A
{
public: 
    A(int v1, int v2)
    {
        if(FunktionDieBeiFehler0Zurückgibt() == 0)
        {
            throw "error";    //???
        }
    }
}

Code:
class B : public A
{
private:
    char* buffer;

public:        
    public B(int v1, int v2) : A(v1, v2)
    {
        buffer = new char[10000];    //Wie mache ich hier die Fehlerbehandung
    }
}

und
Code:
main()
{
    //So würde ich gerne die Fehlerbehandlung als "Benutzer" des Objekts B machen
    B *Objekt;
    try
    {
        Objekt = new B(1, 2);
    }
    catch(...)
    {
        //Wird ausgeführt wenn Objekt nicht erstellt werden kann
    }
}
}

Mein Problem ist jetzt wie ich einen Fehler im Konstruktor bemerke der in den Basisklassen erzeugt wird (wie in A). Ich hab verschiedenes gelesen von dass bei Fehlern das Objekt automatisch sauber zerstört wird (wenn man unique Pointe verwendet) bis hin zu "ohne Funktions try-catch geht nichts". Wie macht man sowas sauber ?
Was passiert außerdem wenn in einem Objekt C in den Variablendeklarationen sowas stehen würde : B Objekt; ? Schlägt dann der Konstruktor von C automatisch fehl ?
 
Eine Möglichkeit sind Exceptions:
Code:
// Standard-Exceptions laden (von std::exception abgeleitet)
#include <stdexcept>
#include <iostream>
// Für std::unique_ptr; braucht C++11
#include <memory>

class A {
public:
    A(int v1, int v2) {
        if(FunktionDieBeiFehler0Zurückgibt() == 0)
        {
            // Statt runtime_error kann man auch eine eigene exception schreiben,
            // sollte man dann von std::exception ableiten.
            throw std::runtime_error("Fehler");
        }
    }
};

class B : public A {
public:
    B(int v1, int v2) : A(v1, v2) {
        // In C++ schmeißt new() eine Exception (std::bad_alloc)
        buffer = new char[10000];
    }

    ~B() {
        delete[] buffer;
    }
    
private:
    char* buffer;
}

int main(int argc, char* argv[]) {    
    try {
        std::unique_ptr<A>(new B(1, 2));
        
        return 0;
    } catch (const std::exception& ex) {
        // Fängt alle Exceptions, die von std::exception abgeleitet sind.
        std::cerr << "Fehler: " << ex.what() << std::endl;
        
        return 1;
    }
}

Bei B machst du gar nichts, da der Konstruktor von A vor dem von B aufgerufen wird. Wenn A also aussteigt, ist B noch gar nicht dran gewesen.

Wenn der Konstruktor eine Exception wirft, wird fast immer ein Destruktor aufgerufen. Du hast also kein halb-kaputtes Objekt danach. Der einzige Fall, wo der nicht automatisch aufgerufen wird ist bei Verwendung von Placement new(). Wer das verwendet, muss dann halt selber aufräumen.
 
Zuletzt bearbeitet:
Kann man also sagen dass egal in welchem Konstruktor in der Klassenhierarchie eine exception geworfen wird (ob von new oder von mir selber), alle bis dahin erstellten Objekte und Variablen automatisch sauber gelöscht werden (durch Aufruf ihres Destruktors) und die exception landet im catch der main-Methode? Die function-try Blöcke dienen nur dazu verschiedene Exceptions unter einem Namen zu werfen, oder?
 
Ich hab mir das jetzt noch mal angesehen, damit ich auch keinen Stuss erzähle. Also:

Q: Welcher Destruktor wird aufgerufen, wenn der Konstruktor eine Exception wirft?
A: Alle bereits vollständig initialisierten Objekte im Scope des Konstruktors (deren Konstruktoren also erfolgreich aufgerufen wurde). Dies betrifft auch eventuelle Basisklassen, da deren Konstruktoren vor dem Konstruktor der Kindklasse aufgerufen werden.

Q: Wird der Destruktor eines Objekts aufgerufen, wenn dessen Konstruktor eine Exception wirft?
A: Nein, da das Objekt nicht vollständig initialisiert wurde (und eigentlich gar nicht existiert).

Q: Muss ich Destruktoren der Membervariablen explizit im Destruktor aufrufen?
A: Nein, der Compiler erzeugt die entsprechenden Aufrufe.

Code:
class Foo {
  private:
    std::vector<string> mVector;

  public:
    ~Foo() {
      // Compiler erzeugt mVector.~Vector()
    }
};
 
Zuletzt bearbeitet:
ok dann ist das jetzt klar.Jetzt hab ich das nächste Problemchen. Ich brauch in meiner Klasse Buffer, die ich gerne auf dem heap anlegen möchte. Meiner Recherche nach kann ich dafür entweder einen Vector oder einen Unique Pointer nehmen, den ich bevorzugen würde.Der Spezifikation nach hat der Unique Pointer aber als Array Spezialisierung keinen copy-Konstruktor. Somit ist die einzige Möglichkeit im Konstruktor neben meinem Attribut einen 2. Unique Pointer anzulegen und danach mit swap() mein Array dem Attribut-unique Pointer zu übergeben, oder ?
 
Ich bin mir nicht ganz sicher, ob ich das richtig verstanden habe. Kannst du bitte mal ein Beispiel posten, was du genau machen willst? Kann auch Pseudocode sein. Ein unique_ptr auf einem Array würde ich nur in Betracht ziehen, wenn ich mit einer C-Library interagieren muss. Das hat für mich erst mal was von schlechtem Design. Immerhin ist ein dynamisches Array T[] genau die Definition von std::vector<T>.
 
Ich benötige in meiner Klasse mehrere Buffer zum zwischenspeichern von Daten. Im Moment habe ich einfach nur einen char* Pointer der auf mit new angelegten Speicher zeigt. Damit bekomm ich aber Probleme wenn der Konstruktor eine Exception wirft, da der Speicher nicht mehr freigegebene wird.

Jetzt wollte ich sowas machen:

Code:
public A
{
    private:
        std:unique_pointer<char[]> Buffer;
    public;
        A()
        {
            Buffer = std::unique_pointer<char[]>(new char[50000]);
        }
}

Du meinst sowas sollte man eher lassen ?

Einen Vektor könnt ich auch nehmen, ich brauch aber nur nen Pointer auf meinen Speicherbereich für memcpy() und die Arraygröße und der vector kann ja noch einiges mehr. Ich hatte mir auch std::array angeschaut aber der belegt entweder den Stack oder wird im Heap nicht automatisch gelöscht.
 
Du kannst einen vector ja einfach kopieren:
Code:
// Erzeuge vector mit 1024 chars
std:.vector<char> buff1(1024);

// Kopiere buff1 nach buff2
std::vector<char> buff2(buff1);

// Ab C++11 kann man auch an das interne Array kommen:
auto ptr = buff2.data();
std:.array ist als Alternative zu "char buff[1024]" gedacht, also für auf dem Stack allozierten Speicher. Was wilslt du den mit memcpy() machen? Greifst du auf eine C Lib zu?
 
Also es geht um eine UDP Verbindung wo ich die Daten vor dem senden verschlüssel. Das ganze läuft so ab: Daten werden per Pointer meinem Verbindungsobjekt übergeben, danach in einen Buffer kopiert worin sie verschlüsselt werden. Der Buffer wird dann der sendto() Funktion übergeben. (Das verschlüsseln macht meine C-Lib).
 
Wie gesagt, mit vector.data() kommst du an den Pointer des internen Arrays, sofern du C++11 nutzt. Sehe da also keinen Grund auf unique_ptr<char[]> zu setzen. Ohne C++11 kann man aber über den Pointer des ersten Elements gehen (char *p = &vec[0];), da std::vector laut Standard auf einem fortlaufenden Speicherbereich arbeiten muss (es gilt &v[n] == &v[0] + n für alle 0 <= n < v.size()).
 
Zurück