Newsgroup: it.comp.lang.c++
Titolo: eccezione all'interno di un costruttore

Vorrei fare una domanda molto "tecnica" agli utenti del NG. Se viene
lanciata un'eccezione all'interno di un costruttore e l'oggetto e' allocato
con new cosa succede ? l'oggetto non viene allocato e la memoria liberata ?
(immagino di si).
Se prima che venisse lanciata l'eccezione vengono allocati degli oggetti con
new e poi viene lanciata l'eccezione, come si regola il compilatore invoca
prima il distruttore e poi dealloca la classe o dealloca la casse e i
riferimenti rimangono pendenti ? (perdita di memoria ?)
Saluti
Ing Giuseppe Monteleone

 wrote

In questo caso, gli oggetti non sono distrutti e ci sara perdita di memoria.
Devi usare oggetti (e non puntatori) che hanno distruttori se vuoi che siano
distrutti quando scattano eccezioni. Per esempio:
class X {
    auto_ptr<Y> apY;
public:
    X()
        : apY(new Y)
        {
            throw 1; // apY e *apY sono distrutti correttamente
        }
};
Claudio Puviani

 wrote in message
news:yM_R9.61829$Q%4.19...@news1.tin.it...

C'e' un ottimo esempio di Bruce Eckel nel secondo volume di "Thinking in
C++", capitolo 7 sulle Exception handling, paragrafo sui Constructors, dove
spiega che normalemente ci sarebbe perdita di memoria a meno che non si
facciano delle azioni "atomiche".
Siccome a spiegare e' complicato, ma una volta letto l'esempio diventa tutto
lampante, copio l'esempio che purtroppo e' commentato in inglese.
Bruce Eckel nel secondo volume di "Thinking in C++", capitolo 7 sulle
Exception handling, paragrafo sui Constructors:
"
When writing code with exceptions, it's particularly important that you
always be asking, "If an exception occurs, will this be properly cleaned
 up?" Most of the time you're fairly safe, but in constructors there's a
problem: If an exception is thrown before a constructor is completed, the
associated destructor will not be called for that object. This means you
must be especially diligent while writing your constructor.
The general difficulty is allocating resources in constructors. If an
exception occurs in the constructor, the destructor doesn't get a chance to
deallocate the resource. This problem occurs most often with "naked"
pointers. For example,
//: C07:Nudep.cpp
// Naked pointers
#include <fstream>
#include <cstdlib>
using namespace std;
ofstream out("nudep.out");
class Cat {
public:
  Cat() { out << "Cat()" << endl; }
  ~Cat() { out << "~Cat()" << endl; }
};
class Dog {
public:
  void* operator new(size_t sz) {
    out << "allocating a Dog" << endl;
    throw int(47);
  }
  void operator delete(void* p) {
    out << "deallocating a Dog" << endl;
    ::delete p;
  }
};
class UseResources {
  Cat* bp;
  Dog* op;
public:
  UseResources(int count = 1) {
    out << "UseResources()" << endl;
    bp = new Cat[count];
    op = new Dog;
  }
  ~UseResources() {
    out << "~UseResources()" << endl;
    delete []bp; // Array delete
    delete op;
  }
};
int main() {
  try {
    UseResources ur(3);
  } catch(int) {
    out << "inside handler" << endl;
  }
} ///:~
The output is the following:
UseResources()
Cat()
Cat()
Cat()
allocating a Dog
inside handler
The UseResources constructor is entered, and the Cat constructor is
successfully completed for the array objects. However, inside Dog::operator
new, an exception is thrown (as an example of an out-of-memory error).
Suddenly, you end up inside the handler, without the UseResources destructor
being called. This is correct because the UseResources constructor was
unable to finish, but it means the Cat object that was successfully created
on the heap is never destroyed.
Making everything an object
To prevent this, guard against these "raw" resource allocations by placing
the allocations inside their own objects with their own constructors and
destructors. This way, each allocation becomes atomic, as an object, and if
it fails, the other resource allocation objects are properly cleaned up.
Templates are an excellent way to modify the above example:
//: C07:Wrapped.cpp
// Safe, atomic pointers
#include <fstream>
#include <cstdlib>
using namespace std;
ofstream out("wrapped.out");
// Simplified. Yours may have other arguments.
template<class T, int sz = 1> class PWrap {
  T* ptr;
public:
  class RangeError {}; // Exception class
  PWrap() {
    ptr = new T[sz];
    out << "PWrap constructor" << endl;
  }
  ~PWrap() {
    delete []ptr;
    out << "PWrap destructor" << endl;
  }
  T& operator[](int i) throw(RangeError) {
    if(i >= 0 && i < sz) return ptr[i];
    throw RangeError();
  }
};
class Cat {
public:
  Cat() { out << "Cat()" << endl; }
  ~Cat() { out << "~Cat()" << endl; }
  void g() {}
};
class Dog {
public:
  void* operator new[](size_t sz) {
    out << "allocating an Dog" << endl;
    throw int(47);
  }
  void operator delete[](void* p) {
    out << "deallocating an Dog" << endl;
    ::delete p;
  }
};
class UseResources {
  PWrap<Cat, 3> Bonk;
  PWrap<Dog> Og;
public:
  UseResources() : Bonk(), Og() {
    out << "UseResources()" << endl;
  }
  ~UseResources() {
    out << "~UseResources()" << endl;
  }
  void f() { Bonk[1].g(); }
};
int main() {
  try {
    UseResources ur;
  } catch(int) {
    out << "inside handler" << endl;
  } catch(...) {
    out << "inside catch(...)" << endl;
  }
} ///:~
The difference is the use of the template to wrap the pointers and make them
into objects. The constructors for these objects are called before the body
of the UseResources constructor, and any of these constructors that complete
before an exception is thrown will have their associated destructors called.
The PWrap template shows a more typical use of exceptions than you've seen
so far: A nested class called RangeError is created to use in operator[ ] if
its argument is out of range. Because operator[ ] returns a reference it
cannot return zero. (There are no null references.) This is a true
exceptional condition - you don't know what to do in the current context,
and you can't return an improbable value. In this example, RangeError is
very simple and assumes all the necessary information is in the class name,
but you may also want to add a member that contains the value of the index,
if that is useful.
Now the output is
Cat()
Cat()
Cat()
PWrap constructor
allocating a Dog
~Cat()
~Cat()
~Cat()
PWrap destructor
inside handler
Again, the storage allocation for Dog throws an exception, but this time the
array of Cat objects is properly cleaned up, so there is no memory leak.
"
Maurus

 Ing Giuseppe Monteleone
Saluti!
   //
   // auto_ptr.cpp
   //
   #include <iostream>
   #include <memory>
   using namespace std;
   //
   // A simple structure.
   //
   struct X
   {
       X (int i = 0) : m_i(i) { }
       int get() const { return m_i; }
       int m_i;
   };
   int main ()
   {
      //
      // b will hold a pointer to an X.
      //
      auto_ptr<X> b(new X(12345));
      //
      // a will now be the owner of the underlying pointer.
      //
      auto_ptr<X> a = b;
      //
      // Output the value contained by
      // the underlying pointer.
      //
      cout << a->get() << endl;
      //
      // The pointer will be deleted when a is destroyed on
      // leaving scope.
      //
      return 0;
   }
Program Output
12345