Konstruktor rzucił wyjatek, pamięć wyciekła

0

Witam,

Chciałam zapytać jakie macie sposoby na radzenie sobie z takimi sytuacjami gdy to konstruktor rzuca wyjątkiem. Przykładowo mamy konstruktor i w nim alokujemy pamięć na 1024b po czym linijka niżej alokujemy X bajtów i tu wyrzucony zostanie wyjatek(brak pamieci). Zgodnie ze standardem - dla obiektów które nie zostały całkowicie powołane do życia nie zostanie wywołany destruktor.

czekam na opinie.
Pozdrawiam.

1

najprościej użyć wskaźników "inteligentnych". Np. boost::scoped_ptr<>

0

A np jeśli nie używamy boosta to jakie są jeszcze metody godne polecenia?

0

Przede wszystkim wyjatek mozna zlapac w srodku konstruktora. Z racji tego co napisalas bardzo niezalecane jest rzucanie wyjatkow przez konstruktor. Gdzies tez widzialem konstrukcje:

try
MyClass()
{...}
catch( xxx )
{}

ale nie pamietam czy bylo to w standardzie c++ czy rozszerzenie np. Microsoft. Tak czy inaczej komentarz byl: w ostatecznosci.

Generalnie to chyba malo kto sprawdza czy jest pamiec, chyba ze rezerwuje jakies wielkie obszary pamieci.

//edit:
Kiedys spedzilem caly dzien na szukaniu naruszenia pamieci przez wyciszony wyjatek w konstruktorze. Polaczone bylo to z auto_ptr i specyficznymi blokami w kodzie. Nie polecam, mozna osiwiec :P

0

To już lepsza odpowiedź ale dla najprostszego przypadku.

Ok przechwyciliśmy wyjątek rzucony przez konstruktor i co dalej możemy zrobić?

  1. Były dwa wywołania new char[] w konstruktorze i drugie nie poszło - to mamy wyciek pierwszego zaalokowanego obszaru.

  2. Zwalniamy pamięć? delete []ptr? - ptr jest prywatnym składnikiem klasy - nie odwołamy się do niego. Tyle tu da się znaleźć komplikacji a nie wiadomo co zrobić.

Inny przykład - w konstruktorze powołujemy do życia dwa obiekty identycznego typu jakiejś innej klasy - jeden z tych powołanych obiektow rzuca wyjątkiem - i co robić jak zwalniać pamięć tych w połowie "żywych-upośledzonych" obiektów.

0

Najpierw 2. Po pierwsze lapiemy wyjatek w konstruktorze, w ktorym jest szansa, ze poleci. W zwiazku z tym zawsze mamy dostep do tych skladowych, ktore alokowane sa dynamicznie. Czyli nigdy nie powstanie "obiekt bez zycia", jak to sie chyba nazywa.

A ogolnie. Jesli juz sprawdzamy czy jest pamiec - co z mojej praktyki jest raczej dosc rzadkie - to znaczy, ze szansa jest i zalezy nam na tym, zeby obsluzyc to prawidlowo. Mozemy wtedy pozwalniac te do tej pory zarezerwowane, wyczyscic niejako obiekt i ustawic jakas flage oznaczajaca, ze obiekt jest zepsuty. Pozniej albo sprawdzac ta flage zewnatrz albo w kazdej metodzie rzucac wyjatek w przypadku proby wykonania operacji na obiekcie zepsutym.

Tak ja bym polecal na podstawie tych zalozen:

  • nie chcemy dopuscic do powstania obiektu martwego, wiec konstruktor nie moze rzucic wyjatku
  • obiekt inicjujacy powinien zwalniac swoje zasoby
  • metody moga rzucac wyjatki i pozostawiamy wywolujacemu decyzje, co w takiej sytuacji zrobic (np. logika zdecyduje sie poinformowac GUI o koniecznosci pokazania komunikatu uzytkownikowi)
0

Może to i dobre - podaj przykład jaki kolwiek.

Anty teza do punktu 2 - pamięć przydzielana w liście inicjalizacyjnej konstruktora - wówczas nawet nie zostanie wykonana zawartość konstruktora w bloku {}
jak rozumiem tu miała być instrukcja
{
// jak nie rzuci wyjatkiem to ustawi flage
bool flagaPtr = true;
}

0

jak nie chcesz boosta to masz jeszcze std::auto_ptr (który nie lubi się z kontenerami).
a jak chcesz łapać wyjątki to tak:

TwojaKlasa::TwojaKlasa() :
    wsakznik1(Null),
    wsakznik2(Null),
    wsakznik3(Null)
{
    try {
        wsakznik1 = new Cos1("dasda");
        wsakznik2 = new Cos2();
        wsakznik3 = new Cos1(wsakznik1);
    } catch( ... ) {
         deleteItems();
         throw;
    }
}

void TwojaKlasa::deleteItems()
{
    delete wsakznik1;
    wsakznik1 = Null;
    delete wsakznik2;
    wsakznik2 = Null;
    delete wsakznik3;
    wsakznik3 = Null;
}

TwojaKlasa::~TwojaKlasa()
{
      deleteItems();
}
0
agnieszka_offline napisał(a)

Może to i dobre - podaj przykład jaki kolwiek.

Nie rozumiem. Przyklad na co?

agnieszka_offline napisał(a)

Anty teza do punktu 2 - pamięć przydzielana w liście inicjalizacyjnej konstruktora - wówczas nawet nie zostanie wykonana zawartość konstruktora w bloku {}

Lista inicjalizacyjna nie jest obowiazkowa.

To co podal MarekR22, to cos w stylu tego co napisalem.

0

Ogólnie bardzo dużo elementów wymaga inicjowania w liście inicjalizacyjnej konstruktora jak stałe, referencje a o dziedziczeniu już nie wspominając.

Znalazłam specjalną konstrukcję try catch dla listy inicjalizacyjnej - nawet to ciekawie wygląda ale nigdy nie słyszałam o takowej a tym bardziej w żadnej z polskich książek się jej nie doszukałam.

http://blog.malcom.pl/2008/10/17/wyjatki-w-konstruktorze/

0
agnieszka_offline napisał(a)

Znalazłam specjalną konstrukcję try catch dla listy inicjalizacyjnej - nawet to ciekawie wygląda ale nigdy nie słyszałam o takowej a tym bardziej w żadnej z polskich książek się jej nie doszukałam.

Nic niezwykłego, część standardu C++ od dobrych 11 lat. 'Polskie' książki o C++ są z założenia do d**y, wielu rzeczy w nich nie ma. 'Język C++' Stroustrupa oczywiście ma to pięknie omówione - podrozdział 14.4.6.1 'Exceptions and Member Initialization'/'Wyjątki i inicjowanie składowej' (tłumaczyć to tłumaczyła kobieta, co tłumaczy żałosny poziom tłumaczenia).

0

Nic nie zwykłego. Tyle lat a jakoś nikt z forumowiczów na ten temat nie wpadł. To że polskie książki nie opisują wszystkich aspektów to jeszcze się nie raz przekonamy o tym.

0
agnieszka_offline napisał(a)

Ogólnie bardzo dużo elementów wymaga inicjowania w liście inicjalizacyjnej konstruktora jak stałe, referencje a o dziedziczeniu już nie wspominając.

To wszystko zaklada, ze w konstruktorze obiektu zawieranego poleci wyjatek. A ja zakladam, ze nie powinien. Wspomniana konstrukcja istnieje pewnie po to, ze nie wszystkie klasy sa nasze, wiec w ostatecznosci, jak juz ktos rzuca wyjatek to musimy sobie z tym sami poradzic, bo ktos poszedl na latwizne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1