exception from constructor

Discussion in 'C++' started by Christopher, Aug 1, 2008.

  1. Christopher

    Christopher Guest

    There is a rumor around the office that it is unacceptable to throw an
    exception from a constructor.
    I have always thrown exceptions from constuctors. It is the only
    method of handling errors!

    Where in the world is this argument coming from?
    Does it have any validity?

    I seek knowledge o gurus of the C++
     
    Christopher, Aug 1, 2008
    #1
    1. Advertising

  2. Christopher

    Guest

    On Jul 31, 4:59 pm, Christopher <> wrote:
    > There is a rumor around the office that it is unacceptable to throw an
    > exception from a constructor.


    What do they recommend for when it's impossible to construct the
    object?

    > I have always thrown exceptions from constuctors. It is the only
    > method of handling errors!


    Of course. It is either an object or not.

    > Where in the world is this argument coming from?
    > Does it have any validity?


    It comes from code that is not exception safe. In that case, any
    exception can leak resources. Banning exceptions in constructors is an
    attempt to get away from such problems.

    Ali
     
    , Aug 1, 2008
    #2
    1. Advertising

  3. Christopher

    Markus Moll Guest

    Hi

    Christopher wrote:

    > There is a rumor around the office that it is unacceptable to throw an
    > exception from a constructor.
    > I have always thrown exceptions from constuctors. It is the only
    > method of handling errors!
    >
    > Where in the world is this argument coming from?
    > Does it have any validity?


    To me, it sounds like they confuse constructors and destructors.
    It is rarely a good idea to throw within a destructor.
    http://new-brunswick.net/workshop/c /faq/dtors.html#faq-11.13

    Markus
     
    Markus Moll, Aug 1, 2008
    #3
  4. Christopher

    James Kanze Guest

    On Aug 1, 2:12 am, wrote:
    > On Jul 31, 4:59 pm, Christopher <> wrote:


    > > There is a rumor around the office that it is unacceptable
    > > to throw an exception from a constructor.


    > What do they recommend for when it's impossible to construct
    > the object?


    An assertion failure? Constructing the object with an invalid
    state, which you test before each use?

    In all but a few, very rare cases, of course, exceptions are
    preferable to these alternatives.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 1, 2008
    #4
  5. James Kanze wrote:
    > In all but a few, very rare cases, of course, exceptions are
    > preferable to these alternatives.


    Of course if the constructor of a class can throw exceptions, some
    care has to be taken when using such a class.

    Example:

    class A
    {
    int* table;
    B b; // constructor can throw

    // Disallow copying and assignment here, or whatever

    public:
    A():
    table(new int[100]) // bad!
    {}

    ~A() { delete[] table; }
    };

    The problem with the above code is that if the constructor of 'b'
    throws an exception, the table is leaked.

    You might try to avoid the problem with an A constructor like this:

    A() try:
    table(new int[100])
    {}
    catch(...)
    {
    delete[] table;
    }

    This is not ok, though. AFAIK trying to access a member variable from
    a constructor catch block is undefined behavior. A practical case can be
    constructed by switching the places of 'table' and 'b' inside A. In this
    case 'b' is constructed first, and if it throws, the catch block will
    attempt to delete an uninitialized pointer (which might not be null!),
    which may cause a segmentation fault.

    The proper way of implementing the constructor is:

    A():
    table(0) // ok
    {
    table = new int[100];
    }

    Now if b throws, nothing will have been allocated yet for the table,
    so nothing leaks. (This is so even if we switch the order of 'table' and
    'b'.)

    Another possibility is to make 'table' a managed pointer (eg.
    auto_ptr, a std::vector or a smart pointer), in which case it's ok to
    initialize it in the initialization list.

    The general rule is: Never allocate unmanaged memory in the
    initialization list.
     
    Juha Nieminen, Aug 1, 2008
    #5
  6. Christopher

    James Kanze Guest

    On Aug 1, 4:04 pm, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > > In all but a few, very rare cases, of course, exceptions are
    > > preferable to these alternatives.


    > Of course if the constructor of a class can throw exceptions,
    > some care has to be taken when using such a class.


    Any time something can throw exceptions, some care has to be
    taken when using it. More to the point: any time an operation
    can fail, some care has to be taken when using it.

    > Example:


    > class A
    > {
    > int* table;
    > B b; // constructor can throw


    > // Disallow copying and assignment here, or whatever


    > public:
    > A():
    > table(new int[100]) // bad!
    > {}


    > ~A() { delete[] table; }
    > };


    > The problem with the above code is that if the constructor of
    > 'b' throws an exception, the table is leaked.


    But the issue above has nothing to do with constructors. You
    get exactly the same problem if you write:

    void
    f()
    {
    int* table = new int[ 100 ] ;
    g() ; // g() can throw...
    }

    Exception safety requires some thought. The alternative ways of
    reporting errors do as well. The general rule is that if there
    is any chance of the client code being able to handle an error
    locally, you use a return value, rather than an exception.
    Except when return values aren't possible: constructors and
    overloaded operators, for example. In the case of constructors,
    the exception has an additional advantage; the object we
    couldn't construct isn't there, so it can't be used by error.

    > You might try to avoid the problem with an A constructor like this:


    > A() try:
    > table(new int[100])
    > {}
    > catch(...)
    > {
    > delete[] table;
    > }


    > This is not ok, though. AFAIK trying to access a member
    > variable from a constructor catch block is undefined behavior.
    > A practical case can be constructed by switching the places of
    > 'table' and 'b' inside A. In this case 'b' is constructed
    > first, and if it throws, the catch block will attempt to
    > delete an uninitialized pointer (which might not be null!),
    > which may cause a segmentation fault.


    I'm not sure, because I've never heard of anyone trying to do
    this.

    The standard way of avoiding the problem (in this case) is to
    replace the "int* table" in the class with std::vector<int>.
    And that doesn't cause any problems.

    > The proper way of implementing the constructor is:


    > A():
    > table(0) // ok
    > {
    > table = new int[100];
    > }


    > Now if b throws, nothing will have been allocated yet for the
    > table, so nothing leaks. (This is so even if we switch the
    > order of 'table' and 'b'.)


    No, the proper way of implementing the constructor is, as I
    said, to replace the int* with std::vector< int >. And more
    generally, to ensure that any member of the class which needs
    cleaning up is a proper class itself, with a destructor which
    does the cleaning up.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 1, 2008
    #6
  7. Christopher

    Anarki Guest

    On Aug 1, 9:13 pm, James Kanze <> wrote:
    > On Aug 1, 4:04 pm, Juha Nieminen <> wrote:
    >
    > > James Kanze wrote:
    > > > In all but a few, very rare cases, of course, exceptions are
    > > > preferable to these alternatives.

    > > Of course if the constructor of a class can throw exceptions,
    > > some care has to be taken when using such a class.

    >
    > Any time something can throw exceptions, some care has to be
    > taken when using it.  More to the point: any time an operation
    > can fail, some care has to be taken when using it.
    >
    > > Example:
    > > class A
    > > {
    > >     int* table;
    > >     B b; // constructor can throw
    > >     // Disallow copying and assignment here, or whatever
    > >  public:
    > >     A():
    > >       table(new int[100]) // bad!
    > >     {}
    > >     ~A() { delete[] table; }
    > > };
    > > The problem with the above code is that if the constructor of
    > > 'b' throws an exception, the table is leaked.

    >
    > But the issue above has nothing to do with constructors.  You
    > get exactly the same problem if you write:
    >
    >     void
    >     f()
    >     {
    >         int* table = new int[ 100 ] ;
    >         g() ;     // g() can throw...
    >     }
    >
    > Exception safety requires some thought.  The alternative ways of
    > reporting errors do as well.  The general rule is that if there
    > is any chance of the client code being able to handle an error
    > locally, you use a return value, rather than an exception.
    > Except when return values aren't possible: constructors and
    > overloaded operators, for example.  In the case of constructors,
    > the exception has an additional advantage; the object we
    > couldn't construct isn't there, so it can't be used by error.
    >
    > > You might try to avoid the problem with an A constructor like this:
    > >     A() try:
    > >       table(new int[100])
    > >       {}
    > >       catch(...)
    > >       {
    > >           delete[] table;
    > >       }
    > > This is not ok, though. AFAIK trying to access a member
    > > variable from a constructor catch block is undefined behavior.
    > > A practical case can be constructed by switching the places of
    > > 'table' and 'b' inside A. In this case 'b' is constructed
    > > first, and if it throws, the catch block will attempt to
    > > delete an uninitialized pointer (which might not be null!),
    > > which may cause a segmentation fault.

    >
    > I'm not sure, because I've never heard of anyone trying to do
    > this.
    >
    > The standard way of avoiding the problem (in this case) is to
    > replace the "int* table" in the class with std::vector<int>.
    > And that doesn't cause any problems.
    >
    > >   The proper way of implementing the constructor is:
    > >     A():
    > >       table(0) // ok
    > >     {
    > >         table = new int[100];
    > >     }
    > > Now if b throws, nothing will have been allocated yet for the
    > > table, so nothing leaks. (This is so even if we switch the
    > > order of 'table' and 'b'.)

    >
    > No, the proper way of implementing the constructor is, as I
    > said, to replace the int* with std::vector< int >.  And more
    > generally, to ensure that any member of the class which needs
    > cleaning up is a proper class itself, with a destructor which
    > does the cleaning up.
    >
    > --
    > James Kanze (GABI Software)             email:
    > Conseils en informatique orientée objet/
    >                    Beratung in objektorientierter Datenverarbeitung
    > 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


    @James

    when is the construction of b done? after

    table = new int[100];

    or before that statement?
     
    Anarki, Aug 2, 2008
    #7
  8. Christopher

    James Kanze Guest

    On Aug 2, 6:33 am, Anarki <> wrote:
    > On Aug 1, 9:13 pm, James Kanze <> wrote:


    [...]
    > > > Example:
    > > > class A
    > > > {
    > > > int* table;
    > > > B b; // constructor can throw
    > > > // Disallow copying and assignment here, or whatever
    > > > public:
    > > > A():
    > > > table(new int[100]) // bad!
    > > > {}
    > > > ~A() { delete[] table; }
    > > > };
    > > > The problem with the above code is that if the constructor of
    > > > 'b' throws an exception, the table is leaked.


    [...]
    > > > The proper way of implementing the constructor is:
    > > > A():
    > > > table(0) // ok
    > > > {
    > > > table = new int[100];
    > > > }
    > > > Now if b throws, nothing will have been allocated yet for the
    > > > table, so nothing leaks. (This is so even if we switch the
    > > > order of 'table' and 'b'.)


    [You really have to learn to trim your citations. I'll
    presume that your questions concern the above snippets,
    since they're the only ones to which it seems to apply, but
    I'm not sure, given the amount of additional material that
    was present.]

    > when is the construction of b done? after


    > table = new int[100];


    > or before that statement?


    Class members are initialized in the order of their declaration
    in the class, using the initializations from the initializer
    list, if present, and before entering the body of the
    constructor. So in the second constructor above:

    - table is initialized to a null pointer value,
    - b is constructed,

    and then we enter the constructor body, where the assignment to
    table takes place.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 2, 2008
    #8
  9. James Kanze wrote:
    > No, the proper way of implementing the constructor is, as I
    > said, to replace the int* with std::vector< int >.


    The problem is if std::vector, or any other STL container, does not do
    what you want, and you are creating your own data container.

    If this is the case, at some point in your code you must explicitly
    write the 'new', and you have to be careful that it doesn't leak in the
    case of an exception.
     
    Juha Nieminen, Aug 2, 2008
    #9
  10. Christopher

    James Kanze Guest

    On Aug 2, 10:16 am, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > > No, the proper way of implementing the constructor is, as I
    > > said, to replace the int* with std::vector< int >.


    > The problem is if std::vector, or any other STL container,
    > does not do what you want,


    Then you create one that does.

    > and you are creating your own data container.


    Then you don't have a second element whose constructor might
    throw.

    > If this is the case, at some point in your code you must
    > explicitly write the 'new', and you have to be careful that it
    > doesn't leak in the case of an exception.


    There are obviously many cases where you need an explicit new.
    That doesn't change anything; whether you use a standard
    container, or write some container or smart pointer or whatever
    of your own. The point is that any given class can only be
    responsible for one resource. The principle of RAII is that the
    responsibility for a resource is always in the hands of a single
    class whose sole role is to be responsible for that resource.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Aug 2, 2008
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Giulio
    Replies:
    9
    Views:
    1,067
    Patrick Kowalzick
    Jun 25, 2003
  2. Brett Irving
    Replies:
    3
    Views:
    3,350
    John Harrison
    Jun 29, 2003
  3. lallous
    Replies:
    5
    Views:
    8,872
    David Harmon
    Jan 23, 2004
  4. Aire
    Replies:
    3
    Views:
    486
    Mike Wahler
    Jan 25, 2004
  5. Generic Usenet Account
    Replies:
    10
    Views:
    2,341
Loading...

Share This Page