Overview
Exception handling in C++ is a powerful mechanism that allows a program to deal with unexpected situations or errors in a controlled manner. It is crucial for writing robust and error-resistant software, as it helps to separate error-handing code from the normal flow of the program, making it easier to understand, maintain, and debug.
Key Concepts
- Try, Catch, and Throw: The fundamental keywords used for exception handling.
- Standard Exception Class: The hierarchy of exception classes derived from
std::exception
. - Resource Management: Using RAII (Resource Acquisition Is Initialization) for managing resources in the presence of exceptions.
Common Interview Questions
Basic Level
- How is exception handling implemented in C++?
- Write a simple C++ program that demonstrates the use of try, catch, and throw.
Intermediate Level
- How do you handle multiple exceptions in C++?
Advanced Level
- Discuss exception safety levels in C++ and how you would achieve them in your code.
Detailed Answers
1. How is exception handling implemented in C++?
Answer: Exception handling in C++ is implemented using three keywords: try
, catch
, and throw
. The try
block contains code that might throw an exception, the throw
keyword is used to throw an exception, and the catch
block is used to handle the exception. If an exception is thrown and not caught within the same function, it propagates (is passed on) to the function that called this function. If no catch block catches the exception, the program terminates.
Key Points:
- try
blocks contain code that might throw exceptions.
- throw
is used to throw an exception.
- catch
blocks are used to handle exceptions.
Example:
#include <iostream>
void mightGoWrong() {
bool errorOccurred = true; // Simulate an error
if (errorOccurred) {
throw "Something went wrong!";
}
}
int main() {
try {
mightGoWrong();
} catch (const char* e) {
std::cout << "Error caught: " << e << std::endl;
}
return 0;
}
2. Write a simple C++ program that demonstrates the use of try, catch, and throw.
Answer: Below is a simple C++ program that demonstrates basic exception handling using try
, catch
, and throw
.
Key Points:
- A function throws an exception using the throw
keyword.
- The main function has a try
block to execute code that might throw an exception.
- The catch
block catches and handles the exception.
Example:
#include <iostream>
void divide(double numerator, double denominator) {
if (denominator == 0) {
throw "Division by zero error!";
}
std::cout << "Result: " << (numerator / denominator) << std::endl;
}
int main() {
try {
divide(10.0, 0.0);
} catch (const char* errorMsg) {
std::cout << "Caught an exception: " << errorMsg << std::endl;
}
return 0;
}
3. How do you handle multiple exceptions in C++?
Answer: In C++, multiple exceptions can be handled by having multiple catch
blocks following a try
block. Each catch
block is designed to handle a specific type of exception, allowing for different types of errors to be handled in different ways. It's important to order the catch blocks correctly, from the most derived exception classes to the least derived (i.e., most specific to most general).
Key Points:
- Use multiple catch
blocks to handle different types of exceptions.
- Order catch
blocks from most specific to most general exception types.
- Catching std::exception
can serve as a catch-all for standard exceptions.
Example:
#include <iostream>
#include <stdexcept> // For std::runtime_error
void riskyFunction(int a) {
if (a == 0) {
throw std::runtime_error("Runtime error occurred");
} else if (a == 1) {
throw "C-style string exception";
}
}
int main() {
try {
riskyFunction(0); // Change the argument to test different exceptions
} catch (const std::runtime_error& e) {
std::cout << "Standard exception caught: " << e.what() << std::endl;
} catch (const char* msg) {
std::cout << "C-style string exception caught: " << msg << std::endl;
} catch (...) {
std::cout << "Catch-all handler for any other exceptions" << std::endl;
}
return 0;
}
4. Discuss exception safety levels in C++ and how you would achieve them in your code.
Answer: Exception safety in C++ can be categorized into four levels: Nothrow, Strong, Basic, and No guarantee. Achieving these guarantees involves careful design to manage object states and resources across exceptions.
Key Points:
- Nothrow Guarantee: The operation guarantees not to throw any exceptions. This is often achieved using noexcept
specifier or by ensuring the operation cannot fail.
- Strong Guarantee: The operation can fail and throw exceptions, but it guarantees that the program state remains unchanged if it does. This often involves transaction-like behavior where changes are only committed if the operation succeeds.
- Basic Guarantee: The operation guarantees that if an exception is thrown, all invariants are preserved and there are no resource leaks. However, the program state may change.
- No Guarantee: No safety guarantees are provided. Throwing an exception could leave the program in an inconsistent state or cause resource leaks.
Example:
To provide a strong guarantee, you might use copy-and-swap idiom for assignment operators:
#include <algorithm> // std::swap
class MyClass {
private:
int* data;
public:
MyClass(int value) : data(new int(value)) {}
~MyClass() { delete data; }
// Copy-and-swap idiom for strong exception safety
MyClass& operator=(MyClass other) {
std::swap(data, other.data);
return *this;
}
// Copy constructor for deep copy
MyClass(const MyClass& other) : data(new int(*other.data)) {}
};
In this example, the assignment operator uses the copy-and-swap idiom, which provides a strong exception guarantee. If copying other.data
throws, the current object remains untouched, thus preserving program state.