简体   繁体   中英

Where am I supposed to catch the C++ exception thrown in the constructor?

Header file:

#ifndef MUTEXCLASS
#define MUTEXCLASS

#include <pthread.h>

class MutexClass
{
private:
    pthread_mutex_t & _mutexVariable;
public:
    MutexClass (pthread_mutex_t &);
    ~MutexClass ();
};

#endif // MUTEXCLASS

Source file:

#include "mutexClass.h"
#include <stdexcept>

MutexClass::MutexClass (pthread_mutex_t & arg) : _mutexVariable (arg)
{
    _mutexVariable  = PTHREAD_MUTEX_INITIALIZER;
    int returnValue = pthread_mutex_lock (&_mutexVariable);
    if (returnValue > 0)
    {
        throw std::logic_error ("Mutex couldn't be locked!");
    }
}

MutexClass::~MutexClass()
{
    pthread_mutex_unlock (&_mutexVariable);
}

Where am I supposed to catch the exception thrown in the constructor?

An exception thrown in a constructor can be handled

  • by the code explicitly creating the object ( try { MutexClass m; ... } catch(const std::logic_error& e) { ... } )
  • by code creating an object that contains as member a MutexClass instance (including as base sub-object... ie by code that creates an object derived from MutexClass )
  • by code calling code doing the creation as exceptions will un-wind the stack until some code handles them

Note that for exceptions thrown in constructor of objects that are members of bigger objects (either for an has-a or a is-a relationship) there is a tricky part. The destructor of the bigger object will not be called if the costruction of a member throws an exception... only the already constructed members will be destroyed before propagating the exception. For example a class like:

struct Foo {
    MyData * data;
    MutexClass m;
    MyData() : data(new int[1000]) { }
    ~MyData() { delete[] data; } // NOT called if m constructor throws
};

will leak memory if MutexClass constructor throws an exception.

Also before writing an exception handler however ask yourself if catching the exception is the right thing to do (ie if you know what to do when that condition occurs). Catching an exception and "hiding" it because you don't know what to do in that case is the worst possible choice.

In the specific case if you cannot lock a freshly created mutex can you expect the system to be still in good enough shape that keeping it running is a good idea?

As with any exception, anywhere up the stack where you can handle the exception. This is no way different from handling exceptions thrown in functions.

At the point of construction

try {
  MutexClass m(arg);
}catch( std::logic_error const & e)
{
}

or if you have a pointer

try {
  MutexClass * m = new MutexClass(arg);
}catch( std::logic_error const & e)
{
}

If you were able work with a pointer , passed to a function , surround the function.

Eg

void funfun ( MutexClass * );

try {
  funfun(new MutexClass(arg));
}catch( std::logic_error const & e)
{
}

If you are going to construct the object in an initializer list:

class A
{
MutexClass mc;
A(pthread_mutex_t & m) try : mc(m)
{
} catch ( std::logic_error const & e )
{
// do something here to handle the failure 
// of mc(m) and the now the failure of A 
// must be handled in the construction point of A 
}
};

But now you have to handle the failure of the constructor of A as well.

Furthermore you should watch out for implicit conversions and copies, and you class is sadly copiable.

void funfun(MutexClass m );   

pthread_mutex & m;
try
{
  void funfun(m);
} catch( std::logic_error const & e )
{
}

Read before throwing from a constructor.

Also don't forget that such constructors are not good as static members. So this type of class may break your program

class maybreak 
{
private:

static MutexClass mc;

// ....

};

unless a wrapper function method is defined to put the construction of the static after your program actually starts ( COFU ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM