Contrived demo to show why functions shouldn't throw exceptions while enclosing the direct owner of a freestore object (this also applies to other low-level, C-style resources like filehandles, ports, locks, etc.) The first example function will leak upon it's exception throw, b/c that function's int-pointer
p directly 'owns' (ie, is solely responsible for the lifetime management of) the int located on the freestore memory it points to... and --
being a C-style pointer -- it.is.
braindead! :DD
The other two functions will
not leak, b/c they instead use
well-behaved, robustly-managed C++ RAII resource handles to their underlying data (similarly-located on the freestore memory). The second example uses a
std::unique_ptr (an RAII resource handle), which always cleans up after itself regardless of the function's exit scenario (ie, throwing or not-throwing, in that case). Same for the third example which uses a
std::vector (again, an RAII resource handle); it will also do proper cleanup by destroying that object at the end of it's enclosing function's scope (or, more-technically: at the end of the vector's
lifetime [so...
copy-elision, etc., may 'move' it out into another scope in some other example's context]).
>tl;dr
Just say no to using old C-style pointers outside of creating your own RAII-compliant resource handle classes (from within your own custom C++ library code, ofc) in the first place. But if you still want/need to, use
std::unique_ptr (or possibly,
std::shared_ptr in some cases) instead for reliable, automatic, resource lifetime management (cf.
>>38228 ).
>muh_resource_handles_do_proper_cleanups.cpp
#include <cstdio>
#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
using std::cerr;
using std::make_unique;
using std::puts;
using std::string;
using std::vector;
// silly custom exception class for demo purposes; inherits from std::exception
class Get_me_out_of_here : public std::exception {
public:
// constructor accepts a const char* that is used to set the exception message
Get_me_out_of_here(const char* message) : msg{message} {}
// overrides the baseclass' what() method to return our message instead
const char* what() const noexcept { return msg.c_str(); }
private:
string msg;
};
void leak(int x) // don't: might leak
{
// C-style pointer
auto p = new int{7}; // wont cleanup itself; requires explicit delete'g
if (x < 0)
throw Get_me_out_of_here{"lol, b/c why not leak?"}; // might leak *p
// ...
delete p; // we might never get here
}
void no_leak(int x)
{
// C++ unique_ptr
auto p = make_unique<int>(7); // C++ RAII-compliant; always cleans itself up
if (x < 0)
throw Get_me_out_of_here{"lol, b/c why not _NOT_ leak?"};
// ...
// no need for delete'g p
} // <== std::unique_ptr cleanup occurs here; end of enclosing function's scope
void no_leak_simplified()
{
vector<int> v(7); // vector, a C++ RAII resource-handle, cleans itself up
// ...
puts("look ma, no leaks! :D");
} // <== std::vector cleanup occurs here; end of enclosing function's scope
int main()
{
// example function call #1 :
try {
leak(-1); // leaks
} catch (Get_me_out_of_here const& e) { // catch, then
// handle our custom exception
cerr << "caught exception: " << e.what() << '\n';
}
// example function call #2 :
try {
no_leak(-1); // doesn't leak
} catch (Get_me_out_of_here const& e) { // catch, then
// handle our custom exception
cerr << "caught exception: " << e.what() << '\n';
}
// example function call #3 :
no_leak_simplified(); // doesn't leak
}
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#e13-never-throw-while-being-the-direct-owner-of-an-object
// https://en.cppreference.com/w/cpp/language/raii
>outputs:
caught exception: lol, b/c why not leak?
caught exception: lol, b/c why not _NOT_ leak?
look ma, no leaks! :D
Edited last time by Chobitsu on 05/10/2025 (Sat) 00:22:22.