Is it good practice to throw an exception in the constructor of a C ++ class?

I have this constructor that throws an exception

GenericSocket::GenericSocket(const string& hostname, const string& servname): _hostname(hostname), _servname(servname) { initHints(); int rv; if((rv = getaddrinfo(_hostname.c_str(), _servname.c_str(), &_hints, &_servinfo)) != 0) { throw GenericSocketException(); } } 

initHints () executes memset _hints and sets some variables.

I am testing it using the Google test platform as follows:

 TEST(CreateObject2, getaddrinfoException) { mgs_addrinfo_return = 1; ASSERT_THROW(new GenericSocket("testhost", "4242"), GenericSocketException); } 

Kernel dump test failed:

 [ RUN ] CreateObject2.getaddrinfoException socket creation failed terminate called after throwing an instance of 'common::GenericSocketException' what(): Socket creation failed [1] 43360 abort (core dumped) ./bin/test_common 

Besides the fact that I don’t know exactly what will go wrong, I suspect that some uninitialized object is being deleted (?), It seems that a lot is happening under the hood, so I started wondering if it is good practice to throw an exception in the constructor . Perhaps it is better to include this function in another function that I can call after creating the object and handle the exception later?

+4
source share
6 answers

IMHO, throwing an exception in the constructor is the best way to handle this situation - do you really want to use a useful object if there is no socket? it doesn't make sense to me. If he fails to resolve this address, there is a reason for this, and it is worth the exception (as long as you handle it correctly!)

In your specific case, you should check the return value and make the exception more useful ... (e.g. HostNotFound - which I think is here)

+11
source

Yes it is. You actually have no choice: constructors have no return values.

But take care of exception safety. See http://www.drdobbs.com/184403429 , for example, Google’s “strong exclusion guarantee”. Indeed, the object created by the constructor will not be destroyed (it never existed) and should be left in a state that does not leak resources.

+6
source

Of course, the only reasonable thing when you cannot build an object is to throw an exception, otherwise you will have some zombie objects. And, answering your other question, no, you cannot destroy an object that was not created.

+5
source

Yes, throwing an exception is the most direct way to report that the constructor has run into problems because they do not return a value.

Destructors, on the other hand, should not throw exceptions, since they will be called during the expansion phase of the exception handling stack and throw another exception at this point, which will lead to interruption.

+4
source

There are at least two discussions about this at SO. About both exceptions from designers and two-phase initialization:

1) When is it right for the constructor to throw an exception?

2) Throwing exceptions from constructors

+1
source

If you learn during construction that you cannot create a valid object, then throwing an exception may be your best option. Thus, the user of the class is prohibited from continuing in the event of an error, unless they can handle it. This is usually the correct behavior of the program.

 function() { // 1. acquire resources. // 2. perform action. // 3. release resources. } 

If you cannot complete the first step, other steps are useless. This is what we use RAII for: we first acquire all the resources we need, somewhat similar to the old C programming style with respect to stack variables. With RAII, if any of the resources cannot be obtained - through an exception in the constructor - then all previously acquired resources will be automatically released, and the second step will never be taken. Everything is done behind the scenes automatically, but this assumes that you throw an exception in the constructor if you cannot create the object. It also assumes that the destructor performs the cleanup.

 struct Connection { Connection() { if( ! connect() ) throw ConnectionError(); } ~Connection() { // may not throw under any circumstances try { disconnect(); } catch( ... ) { } } void send(const std::string& message); // ... private: bool connect(); // ... }; void post(const std::string& message) { // step one is easy for the user: Connection c; // step two is the bulk of the work: c.send("HELO"); // step three is automatically done for the user. } 
+1
source

Source: https://habr.com/ru/post/1334260/


All Articles