C++ is old-fashioned. C++ has no garbage collection. No garbage collection? Right! Old fashioned? Wrong!
What is against garbage collection in C++? At first, garbage collection breaks one of the key principles of C++: "Don't pay for something you don't use." That means if you don't need garbage collection your C++ runtime should not waste its time cleaning up the whole garbage. My second point is more sophisticated.
We have RAII in C++ and therefore the deterministic destruction of objects. But, what is RAII? That's the topic of this post.
Resource Acquisition Is Initialization
RAII stand for Resource Acquisition Is Initialization. Probably, the most important idiom in C++ says that a resource should be acquired in the constructor of the object and released in the destructor of the object. The key is that the destructor will be automatically called if the object goes out of scope. If this is not totally deterministic? In Java or Python (__del__) you have a destructor but not the guarantee. Therefore, it can end disastrous, if you use the destructor to release a critical resource like a lock. That's a classical anti-pattern for a deadlock. But in C++, we are on the safe side.
The example shows the totally deterministic behavior of RAII in C++.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
// raii.cpp
#include <iostream>
#include <new>
#include <string>
class ResourceGuard{
private:
const std::string resource;
public:
ResourceGuard(const std::string& res):resource(res){
std::cout << "Acquire the " << resource << "." << std::endl;
}
~ResourceGuard(){
std::cout << "Release the "<< resource << "." << std::endl;
}
};
int main(){
std::cout << std::endl;
ResourceGuard resGuard1{"memoryBlock1"};
std::cout << "\nBefore local scope" << std::endl;
{
ResourceGuard resGuard2{"memoryBlock2"};
}
std::cout << "After local scope" << std::endl;
std::cout << std::endl;
std::cout << "\nBefore try-catch block" << std::endl;
try{
ResourceGuard resGuard3{"memoryBlock3"};
throw std::bad_alloc();
}
catch (std::bad_alloc& e){
std::cout << e.what();
}
std::cout << "\nAfter try-catch block" << std::endl;
std::cout << std::endl;
}
|
ResourceGuard is a guard that managed its resource. In this case, the resource is a simple string. ResourceGuard creates in its constructor (line 11 - 13) the resource and releases the resource in its destructor (line 14 - 16). It does its job very reliable.
The destructor of resGuard1 (line 23) will be exactly called at the end of the main function (line 46). The lifetime of resGuard2 (line 27) already ends in line 28. Therefore, the destructor will automatically be executed. Even the throwing of an exception does not alter the reliability of resGuard3 (line 36). Its destructor will be called at the end of the try block (line 35 - 38).
The screenshot displays the lifetime of the objects.

I will refer once more to the program raii.cpp. What is the key idea of the RAII idiom? The lifetime of a resource will be bound to the lifetime of a local variable. C++ automatically manages the lifetime of locals.
The idea is quite simple but has far-reaching consequences. Critical resources are bound to local objects. The remaining job is done by the C++ runtime.
RAII everywhere
RAII everywhere holds for locks std::lock_guard, std::unique_lock, and std::shared_lock that manage their resource mutex. RAII everywhere holds for the smart pointers std::unique_ptr, std::shared_ptr, and std::weak_ptr that manage their resource memory.
Thanks to RAII we have in C++ a kind of garbage collection.
But there is a subtle difference to a general garbage collection.
- You have to explicitly use smart pointers in C++
- The memory management with std::unique_ptr has by design no overhead in performance or memory compared to a raw pointer (see Memory and Performance Overhead of Smart Pointers).
Therefore, C++ adheres to its key principle in a twofold way: Don't pay for something you don't use.
Modernes C++ Mentoring
Be part of my mentoring programs:
Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.
Special resources
Thanks to RAII, the destructor of the local object and therefore the cleaning up of the resource will be done totally deterministic. So far, so good. If the destructor can throw an exception, the destructor of the object modeling RAII will trigger the exception. This will be the case if the resource is a file. The close function can trigger an exception. Therefore, you have to answer the question for yourself, if it's ok, that the destructor can throw an exception. If not, you should not use RAII.
Dealing with throwing destructors (Udo Steinbach)
Udo Steinbach wrote an e-mail to me about the issue with potentially throwing destructors. Here is the e-mail.
RAII is a nice thing − as long as no error can occur while destroying the object. The latter is forgotten when talking about RAII. Why a destructor should not throw, you can read in many places: https://www.qwant.com/?q=should%20destructors%20throw. As a result, RAII has to be supplemented manually in many cases and so it seems to be done twice.
class MyFileHandle
{ public:
MyFileHandle(...)
:handle(::OpenFile(...))
{ if (handle == nullptr)
throw ...;
}
~MyFileHandle() noexcept
{ ::CloseFile(handle);
}
private:
MySystemHandle handle;
};
{
MyFileHandle file(...);
...
}
Does CloseFile() not close, the call looks correct but the handle is lost, the user has to restart the program and search and delete the file himself, or other embarrassing symptoms as they are familiar from programs.
So the class must have a throwing
void Close();
and the destructor has to check:
{
MyFileHandle file(...)
...
file.Close();
}
This doesn't look like pure RAII. For symmetry we need a manual Open():
{
MyFileHandle file;
file.Open(...);
...
file.Close();
}
− RAII is lost. For the lover remains the consolation, that the object is now reusable and runs correctly in both non-error and error cases.
Under the condition of proper error handling from the perspective of the program user, I renounce RAII in many of my classes. Automatic tests according to an idea from boost show
-
exception safety as a minimum,
-
with best-known error handling, which must be dispensed with at RAII, e.g. automatically delete incomplete files and rethrow the exception,
- and messages presentable to the user,
an always best possible behavior of the program: Make users and support happy by replacing crash and data garbage with meaningful messages. An automatism, here destructor or garbage collector, can handle errors only automatically: on a minimalistic scale or ignore it completely.
In application programs this should not be accepted nor should it be.
The famous last words (Bjarne Stroustrup)

Bjarne Stroustrup made a short remark to my news about this post on C++Enthusiasts:
"Things are still improving": http://www.stroustrup.com/resource-model.pdf
So what is this 20 pages paper about, written by Bjarne Stroustrup, Herb Sutter, and Gabriel Dos Reis. I provide you with a screenshot of the abstract and the overview. You have to read the very readable paper by yourself.

I promise I will write in future posts about the Core C++ Guidelines. But you have to be patient. I first have to catch up with my German blog.
What's next?
With the next post, I enter the area of C++ experts. I write about explicit memory management in C++.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.
My special thanks to Embarcadero 
My special thanks to PVS-Studio 
My special thanks to Tipi.build 
My special thanks to Take Up Code 
Seminars
I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Bookable (Online)
German
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ - The Core Language
- C++ - The Standard Library
- C++ - Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
New
- Clean Code with Modern C++
- C++20
Contact Me
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: This email address is being protected from spambots. You need JavaScript enabled to view it.
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++,

Comments
Lack of GC force the developer to thing about the design. Example of this could be multithred/agent application which tasks exchange data by pointers. So developer needs to think who will take responsibility of orphaned task, especially if we assume that task can close itself or it can be closed by other. From my perspective this forced design allows better re-use of components.
In the end, programming languages have to deal with asynchronous events and developers have to know how to do this in a way that works. The no-acquires-in-your-constructor rule means that every action that acquires another object reference or somehow is part of another objects life cycle should be an explicit part of your API design after construction. In the class shown here, MyFileHandle should explicitly acquire the reference from ::OpenFile in an API that returns the failure explicitly as a documented part of the API. Whether it's an exception or a return value, there should be explicit steps involved in managing communication of the failure to open.
This means that each API should involve at most one acquire (use a more complex object that you wrap multiple steps into if you need that) or event failure.
Bad software design is what creates the majority of these problems and heated discussions.
The beauty of Java GC is that it doesn't require you to change your coding style just because you declare something locally, but pass it to some other object to manage the lifecycle thereof. Thus, dependency injection is readily done without value vs pointer vs reference code syntax and lots of other such details.
Better yet, Java eliminates uninitialized data references since all class members are initialized to their default value whether you do it explicitly or not. That's the #1 benefit to software construction it has.
The RuntimeException class augments the Throwable class with the ability to not worry about exceptions that may occur due to null pointers or NaN values etc. Whereas the Exception class extends Throwable with the ability to declare explicitly and demand handling of error types which are not handled anywhere else and must be dealt with by the developer.
MyFileHandle would be forced to throw IOException in the constructor. The developer would deal with the constructor not working.
Java Exceptions provide the best way to make sure that developers are told about the wrong things they are doing with error handling. Unfortunately, most people believe that explicit error declarations are too much of a problem because they don't understand how and when asynchronous events can happen in software interfacing with the outside world.
RSS feed for comments to this post