The C++ constructor throws an exception

  • 2020-11-03 22:32:08
  • OfStack

Syntactically, constructors can throw exceptions. But logically and risk-controllably, constructors try not to throw exceptions. As a last resort, 1 must be careful to prevent memory leaks.

1. Constructor throws an exception causing a memory leak

In the C++ constructor, special care is taken to prevent memory leaks when you need to allocate memory and throw exceptions. Because an exception is thrown in the constructor, it is conceptually treated as if the object was not successfully constructed, so the destructor of the current object is not called. Also, since the constructor itself is a function, throwing an exception inside the function will result in the end of the current function run and the release of constructed member objects, including members of its base class, that execute destructors of both the immediate base class and the member object. Consider the following procedure.


#include <iostream>
using namespace std;

class C
{
int m;
public:
C(){cout<<"in C constructor"<<endl;}
~C(){cout<<"in C destructor"<<endl;}
};

class A
{
public:
A(){cout<<"in A constructor"<<endl;}
~A(){cout<<"in A destructor"<<endl;}
};

class B:public A
{
public:
C c;
char* resource;

B()
{
resource=new char[100];
cout<<"in B constructor"<<endl;
throw -1;
}
~B()
{
cout<<"in B destructor"<<endl;
delete[] resource;
}
};

int main()
{
try
{
B b;
}
catch(int)
{
cout<<"catched"<<endl;
}
}

Program output results:

[

in A constructor
in C constructor
in B constructor
in C destructor
in A destructor
catched

]

As you can see from the output, if an exception is thrown in the constructor, the destructor of the current object will not be called, and if memory is allocated in the constructor, it will cause a memory leak, so be careful.

In addition, when you construct object b, you first execute the constructor of its immediate base class A, then the constructor of its member object c, and then enter the constructor of class B. Because an exception was thrown in the constructor of class B, which was not caught in the constructor, the execution of the constructor of class B was interrupted and the object b was not constructed. During the "rollback" of the constructor of class B, the destructor of class c and the destructor of class A are called one after another. Finally, since b was not constructed successfully, the main() function does not call the destructor of b at the end of the function, leaving it vulnerable to memory leaks.

2. Use smart Pointers to manage memory resources

Memory leaks can be avoided by using RAII (Resource Acquisition is Initialization) technology. RAII refers to resource acquisition and initialization, that is, applying for allocation of resources in the constructor and releasing resources in the destructor. Because C++ 's language mechanism ensures that constructors are automatically called when an object is created, and destructors are automatically called when the object is out of scope. Therefore, under the guidance of RAII, we should use classes to manage resources and bind resources to the life cycle of objects. Smart Pointers are the most representative implementation of RAII. Using smart Pointers, you can achieve automatic memory management, and you no longer need to worry about memory leaks caused by forgetting delete.

Therefore, when the constructor is forced to throw an exception, the "smart pointer" unique_ptr can be used to prevent memory leaks. Refer to the following program


#include <iostream>
using namespace std;

class A
{
public:
A() { cout << "in A constructor" << endl; }
~A() { cout << "in A destructor" << endl; }
};

class B
{
public:
unique_ptr<A> pA;
B():pA(new A)
{
cout << "in B constructor" << endl;
throw - 1;
}
~B()
{
cout << "in B destructor" << endl;
}
};

int main()
{
try
{
B b;
}
catch (int)
{
cout << "catched" << endl;
}
}

Program operation Results:

[

in A constructor
in B constructor
in A destructor
catched

]

From the perspective of the program's running results, through intelligent pointer management of memory resources, the destructor of A class is still called at the end of object pA's life cycle to avoid resource leakage, although the exception thrown by the constructor of class B causes the destructor of class B to not be executed.

That's where the C++ constructor throws exceptions, and more on C++ constructors can be found in other articles on this site!


Related articles: