![]() |
VOOZH | about |
In modern C++, developers aim to write programs that do not easily break, can be reused in different situations, and run efficiently. Two important features that help achieve this are:
In C++, exceptions are handled using three keywords:
Example:
Example:
This function works for int, float, and other types that support the + operator.
C++ provides a hierarchy of standard exceptions in the <exception> and <stdexcept> headers. These are predefined classes derived from the base class std::exception and can be used to represent common errors.
Some commonly used standard exceptions:
Example:
Using standard exceptions makes your code consistent and compatible with STL and third-party libraries.
You can create a custom exception by defining a class that inherits from the std::exception class and overriding its what() method, which returns an error message.
Example:
Benefits:
A function template allows a function to operate on generic data types. It provides a way to write one function for multiple types, which is resolved at compile-time.
Syntax:
RAII (Resource Acquisition Is Initialization) is a design pattern where resources (like memory, file handles, sockets) are acquired in the constructor of an object and released in its destructor. RAII ensures automatic cleanup, even if an exception is thrown, making code exception-safe.
Example:
In this example, even if an error occurs, the file is properly closed when the object goes out of scope.
The catch(...) block is used to catch any type of exception, regardless of its data type or class. It acts as a generic fallback handler.
Example:
Difference:
Itβs helpful when you want to ensure that no exception escapes unhandled, but it does not provide specific details about the exception type.
inline | constexpr |
|---|---|
| Suggests to the compiler to expand code at call site | Indicates that the value/function can be evaluated at compile time |
| Used for small functions to reduce function call overhead | Used for constant expressions and compile-time evaluation |
| No guarantee of compile-time evaluation | Must be evaluated at compile time (if possible) |
| Can return any type, computed at runtime or compile-time | Must return a constant value, known at compile time |
| C++98 | C++11 |
| Performance optimization | Defining compile-time constants and expressions |
| Can contain any valid code | Limited to constant expressions and no side effects |
| inline | constexpr |
|---|---|
| Suggests the compiler to replace function | Ensures compile-time evaluation |
| Any | Must return a value known at compile time |
| Available since C++98 | Introduced in C++11 (improved in C++14/17/20) |
Example constexpr:
Example inline:
Use constexpr when the value is needed at compile-time, and inline when you want faster execution without function call overhead.
Yes, C++ allows catching derived exceptions using a base class reference (std::exception &). But object slicing or loss of specific information can occur if exceptions are not caught by reference or pointer.
Example:
If an exception is thrown but not caught anywhere in the call stack, the program calls std::terminate(), which by default aborts execution. Exceptions can propagate up the call stack until a suitable catch block is found. If none is found, the program terminates.
Example:
Output:
terminate called after throwing an instance of 'int'
Aborted (core dumped)
This can lead to abrupt termination, loss of data, or unclean resource release. That's why it's critical to ensure every throw has a matching catch, either directly or through stack unwinding.
Stack unwinding is the process of cleaning up the call stack after an exception is thrown and before it is caught. During unwinding, destructors of all local objects are called in reverse order of construction, ensuring proper cleanup. This prevents resource leaks and enforces RAII (Resource Acquisition Is Initialization).
Why is it important?
Example:
Output:
Constructor
Destructor
Exception caught
This shows that even when an exception occurs, destructors are called properly to clean up resources.
Example:
throw; preserves the original exception type and stack trace, which is important for advanced debugging and exception chaining.
The noexcept keyword specifies that a function does not throw exceptions. It makes intent clear to both the compiler and developers. If a noexcept function does throw, std::terminate() is called. noexcept is especially important for move constructors and destructors, where it enables optimizations such as exception-safe move operations in standard containers.
Example:
Benefits:
noexcept is especially important in move constructors and destructors in modern C++.
Throwing an exception from a destructor during stack unwinding (i.e., while handling another exception) results in a call to std::terminate(), which aborts the program.
Example:
Avoid throwing from destructors, especially when exceptions are active. Use std::uncaught_exceptions() (C++17) or try-catch inside destructor to suppress.
Function templates cannot be partially specialized, only fully specialized. Partial specialization is only supported for class templates. To achieve similar behavior with functions, you can use overloading, SFINAE (Substitution Failure Is Not An Error), or tools like std::enable_if and if constexpr to tailor behavior for specific categories of types.
Using std::enable_if, we mimic specialization behavior for functions based on type traits, allowing cleaner and type-specific implementations.
Throwing a pointer means you're throwing an address. It wonβt trigger automatic destruction of the object pointed to, and catching it requires catching the same pointer type. Throwing by value creates a copy, and cleanup is automatic.
Throwing objects ensures RAII and automatic cleanup. Throwing pointers must be handled with care to avoid memory leaks.
Yes. Templates can use static_assert or if constexpr to control logic at compile-time based on type, and can throw exceptions conditionally at runtime.
Example:
Templates can adapt behavior based on type at compile time. Combining this with runtime exception logic adds powerful type-aware safety mechanisms.