Can I use exceptions in the constructor?

Hello

Sorry for my English in advance!

Can I throw exceptions from the constructor?

I want to send data to the Serial Port. And I want to control errors via exceptions. I've written my own exception for it:

PortError.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef PORTERROR_H
#define PORTERROR_H

#include <stdexcept>
#include <string>

class PortError : public std::runtime_error
{
public:
    PortError( const std::string &errorText ) : std::runtime_error( "" )
    {
        m_message = "Error: " + errorText;
    }

    virtual ~PortError() throw()
    {

    }

    virtual const char *what() const throw()
    {
        return m_message.c_str();
    }

    std::string getMessage()
    {
        return m_message;
    }

private:
    std::string m_message;
};

#endif // PORTERROR_H 


I will use it like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void MainWindow::on_startTransmissionButton_clicked()
{
    QString text = ui->valueForSendingLineEdit->text();
    QByteArray data;
    data.append( text );
    try {
        m_sender->send( data );
    } catch ( const PortError &e ) {
        QMessageBox::information( this, "Error", QString( e.what() ) );
        return;
    } catch ( ... ) {
        QMessageBox::information( this, "Error", "Error: unknown exception" );
        return;
    }
}


Sender.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef SENDER_H
#define SENDER_H

#include <QSerialPort>
#include <QString>
#include "PortError.h"

class Sender
{
public:
    Sender( const QString &portName,
            QSerialPort::BaudRate baudRate = QSerialPort::Baud9600,
            QSerialPort::DataBits dataBits = QSerialPort::Data8,
            QSerialPort::Parity parity = QSerialPort::NoParity,
            QSerialPort::StopBits stopBits = QSerialPort::OneStop,
            QSerialPort::FlowControl flowControl = QSerialPort::NoFlowControl );

    ~Sender();

    void send( const QByteArray &data ) throw( PortError );

private:
    QSerialPort m_port;
    QString m_portName;
    QSerialPort::BaudRate m_baudRate;
    QSerialPort::DataBits m_dataBits;
    QSerialPort::Parity m_parity;
    QSerialPort::StopBits m_stopBits;
    QSerialPort::FlowControl m_flowControl;
};

#endif // SENDER_H 


Sender.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "Sender.h"

Sender::Sender( const QString &portName,
                QSerialPort::BaudRate baudRate,
                QSerialPort::DataBits dataBits,
                QSerialPort::Parity parity,
                QSerialPort::StopBits stopBits,
                QSerialPort::FlowControl flowControl ) :
    m_port( portName ),
    m_baudRate( baudRate ),
    m_dataBits( dataBits ),
    m_parity( parity ),
    m_stopBits( stopBits ),
    m_flowControl( flowControl )
{
    // Set the port name
    m_port.setPortName( m_portName );

    // Open the port
    if ( !m_port.open( QIODevice::WriteOnly ) ) {
        throw PortError( m_port.errorString().toStdString() );
    }

    m_port.setBaudRate( m_baudRate );
    m_port.setDataBits( m_dataBits );
    m_port.setParity( m_parity );
    m_port.setStopBits( m_stopBits );
    m_port.setFlowControl( m_flowControl );
}

Sender::~Sender()
{
    m_port.close();
}

void Sender::send( const QByteArray &data ) throw( PortError )
{
    // Write data to the port
    if ( m_port.write( data ) == -1 ) {
        throw PortError( m_port.errorString().toStdString() );
    }
}
Last edited on
Can I use it? Is it right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    try {
        m_sender = new Sender( "COM1" );
    } catch ( const PortError &e ) {
        QMessageBox::information( this, "Error", QString( e.what() ) );
    } catch ( ... ) {
        QMessageBox::information( this, "Error", "Error: unknown exception" );
    }
}
> Can I throw exceptions from the constructor?

Yes, you can. (You would need to take extra care for objects which may be placed at namespace scope.)

See: http://www.parashift.com/c++-faq/ctors-can-throw.html
And: http://www.parashift.com/c++-faq/selfcleaning-members.html

Details: http://www.gotw.ca/publications/mill13.htm
Thank you!

My application crash when I call .isOpen() Method. Why? Who can say the reason?

1
2
3
4
5
6
7
8
9
10
11
void Sender::send( const QByteArray &data ) throw( PortError )
{
    if ( !m_port.isOpen() ) {
        throw PortError( m_port.errorString().toStdString() );
    }

    // Write data to the port
    if ( m_port.write( data ) == -1 ) {
        throw PortError( m_port.errorString().toStdString() );
    }
}
Last edited on
> My application crash when I call .isOpen() Method. Why? Who can say the reason?

You should probably post this question in one of the Qt forums. http://qt-project.org/forums/

Google produced 251,000 results in 0.35 seconds for QSerialPort+crash
https://www.google.com/search?newwindow=1&q=QSerialPort+crash
So, it appears that crash is the norm rather than the exception for this particular library.
1
2
3
4
5
6
7
    try {
        m_sender = new Sender( "COM1" );
    } catch ( const PortError &e ) {
        QMessageBox::information( this, "Error", QString( e.what() ) );
    } catch ( ... ) {
        QMessageBox::information( this, "Error", "Error: unknown exception" );
    }

I see you didn't delete m_sender if the constructor throws. That's good because the compiler deletes it.... Wait.....

I mean you need to delete it because it was constructed...

Hmm. But it wasn't constructed because you threw an exception.... hey just what DOES this do??

Remember if you ever declare one of these as a static or global that you have to surround it with a try..catch block:
1
2
3
try {
   static Sender s("com1");
} catch {


Uh. Wait. Will that work?? Won't s be defined and deleted inside that try block? Can you really do this at file scope? You have to right? I mean, how can you create one of these at file scope without it right? But it's code in a declaration block.

Hmm....

And what if you have a Sender it as a member of another class:
1
2
3
4
5
6
7
8
class MySender:
   string name n;
   Sender s;
   OtherStuff  os1;
    MySender(const string &nm) :
        name(n), s("com1"), os1("somedata") {;}
   ~MySender();
}


Now in the MySender destructor you have to be careful because if the Sender member's contructor throws an exception then the other members aren't constructed.... I mean they get their default constructor... I mean unless OtherStuff's constructor throws, in which case....

uh......

Confused? Me too. :)

Issues like these are why it's a bad idea to throw exceptions in a constructor. It can be done, and there are well defined semantics for what happens in the cases I've described. But do you know the rules? Will the next person who sees your code know the rules? I know I don't.

So unless there is a compelling reason why you need to throw in the constructor, I'd avoid it. Create an open() method instead. It makes the class more flexible and useable.
> Confused? Me too. :)

Yes. You clearly do not have the faintest clue about object life-times in C++.


> I see you didn't delete m_sender if the constructor throws.
> ... blah blah blah...

The fix is embarrassingly simple.
Where appropriate, use automatic storage duration.
Where it is not possible, encapsulate resource acquisition and release; use RAII; use smart pointers.


> And what if you have a Sender it as a member of another class: ...
> Now in the MySender destructor you have to be careful because if the Sender
> member's contructor throws an exception then the other members aren't
> constructed.... I mean they get their default constructor...
> I mean unless OtherStuff's constructor throws, in which case....

This is utter rubbish. Ignore him, for he knows not that he knows not.


> Create an open() method instead. It makes the class more flexible and useable.

Nonsense. While there are rare situations where two-phase construction could be a reasonable design decision, it is best avoided in most cases. Demanding initialisation via two-phase construction makes a class more brittle, less usable, the code becomes more error-prone, and less maintainable.
You clearly do not have the faintest clue about object life-times in C++..... This is utter rubbish.

It was meant to be :) The point of my post was that there are a lot of subtle considerations when throwing an exception in a constructor.

While there are rare situations where two-phase construction could be a reasonable design decision, it is best avoided in most cases. Demanding initialisation via two-phase construction makes a class more brittle, less usable, the code becomes more error-prone, and less maintainable.


I respectfully disagree with base, oddly enough, on the same reasons that you raise.
> there are a lot of subtle considerations when throwing an exception in a constructor.

There actually aren't any.

This is the simplest way to write this class; and notwithstanding that its constructor may throw at three different places, it is completely exception-safe.

1
2
3
4
5
6
7
8
9
10
struct person
{
      person( const std::string& n, const std::string& a ) : name(n), address(a) 
     { std::clog << "person::constructor\n" ; }
      // CopyAssignment, MoveAssignment and Destructor are implicit

      private: 
           std::string name ;
           std::string address ;
};


Go through contortions to avoid throwing any exception from the constructor and the code would transmogrify into something grotesque, horrendously verbose, brittle, unmaintainable and error-prone.

Terror-stricken by the very thought of exceptions, if one embraces the abomination of two-phase or multi-phase initialisation of person objects, and ....
Topic archived. No new replies allowed.