In C++, exceptions are not limited to built-in data types. We can also define our own exception types using classes and throw objects of those classes. This allows developers to represent application-specific error conditions in a structured and type-safe way.
A user-defined exception is thrown using:
throw ClassName();
Example 1: Exception Handling with a Single Class
#include <iostream>
using namespace std;
class demo {
};
int main() {
try {
throw demo();
}
catch (demo d) {
cout << "Caught exception of demo class\n";
}
}
Output
Caught exception of demo class
Explanation:
- demo is an empty user-defined class.
- An object of demo is thrown using throw demo().
- The corresponding catch block catches the exception based on its type.
- The exception is handled successfully without program termination.
Example 2: Exception Handling with Multiple Classes
#include <iostream>
using namespace std;
class demo1 {
};
class demo2 {
};
int main() {
for (int i = 1; i <= 2; i++) {
try {
if (i == 1)
throw demo1();
else
throw demo2();
}
catch (demo1 d1) {
cout << "Caught exception of demo1 class\n";
}
catch (demo2 d2) {
cout << "Caught exception of demo2 class\n";
}
}
}
Output
Caught exception of demo1 class Caught exception of demo2 class
Exception handling with inheritance
When exception classes are related through inheritance, the order of catch blocks becomes important.
#include <iostream>
using namespace std;
class demo1 {
};
class demo2 : public demo1 {
};
int main() {
for (int i = 1; i <= 2; i++) {
try {
if (i == 1)
throw demo1();
else
throw demo2();
}
catch (demo1 d1) {
cout << "Caught exception of demo1 class\n";
}
catch (demo2 d2) {
cout << "Caught exception of demo2 class\n";
}
}
}
Output
Caught exception of demo1 class Caught exception of demo1 class
Explanation:
- demo2 is derived from demo1.
- The catch (demo1) block appears before catch (demo2).
- Since base-class handlers match derived objects, the demo2 exception is caught by demo1.
- Best practice: Always place derived-class catch blocks before base-class handlers.
Exception handling with constructor
Exceptions can also be thrown and handled inside constructors.
#include <iostream>
using namespace std;
class demo {
int num;
public:
demo(int x) {
try {
if (x == 0)
throw "Zero not allowed";
num = x;
show();
}
catch (const char* exp) {
cout << "Exception caught\n";
cout << exp << endl;
}
}
void show() {
cout << "Num = " << num << endl;
}
};
int main() {
demo(0);
cout << "Again creating object\n";
demo(1);
}
Output
Exception caught Zero not allowed Again creating object Num = 1
Explanation:
- The constructor validates input before initializing the object.
- When x == 0, an exception is thrown and caught inside the constructor.
- Object creation continues normally for valid input (x = 1).
- Constructors cannot return values, but exceptions allow error signaling during object creation.
Key Points to Remember
- C++ allows throwing objects of user-defined classes as exceptions.
- Exception handling is type-based, enabling fine-grained control.
- In inheritance-based exceptions, catch order matters.
- Constructors can throw and handle exceptions for input validation.
- User-defined exceptions improve clarity, maintainability, and robustness.