Show Lecture.RAII as a slide show.
CS253 RAII
Worst Acronym Ever
RAII means:
- Resource
- Acquisition
- Is
- Initialization
Very Slightly Better
CADRE means:
- Constructor
- Acquires
- Destructor
- Releases
Definition
- RAII means: acquire the resource when you create the variable,
and let the dtor release the resource.
- If you catch yourself thinking “must remember to clean this up”,
then you should be using RAII.
- The object remembers to do the cleanup.
Non-RAII code
Here’s some non-RAII code:
// Acquire resources
FILE *host = fopen("/etc/hostname", "r");
float *scores = new float[253];
char *name = new char[50];
// Use resources
cout << "hi there\n";
// Clean up after ourselves
delete[] name;
delete[] scores;
fclose(host);
hi there
RAII code
The equivalent RAII code:
// Acquire resources
ifstream host("/etc/hostname");
unique_ptr<float[]> scores(new float[253]);
string name(50, 'X');
// Use resources
cout << "hi there\n";
// Clean up after ourselves
hi there
Even better
Sure, unique_ptr does the job. This works fine:
unique_ptr<float[]> scores(new float[253]);
scores[5] = 70;
cout << scores[5];
70
However, vector works just as well, and reads a lot better:
vector<float> scores(253);
scores[5] = 70;
cout << scores[5];
70
So does array (for compile-time sizes):
array<float,253> scores;
scores[5] = 70;
cout << scores[5];
70
As does a plain old C array (for compile-time sizes):
float scores[253];
scores[5] = 70;
cout << scores[5];
70
Why RAII?
It’s not too difficult to remember to free our
resources, but what if something gets in the way?
float *scores = new float[253];
if (getuid() != 0) {
cerr << "Must be super-user!\n";
return 1; // Forgot to delete scores!
}
cout << "hello\n";
delete[] scores;
Must be super-user!
Sure, we could add delete[] scores
to the if clause, but what if
there are several early returns, and many variables? It’s not very
DRY. Also, what about exceptions?
Why RAII?
unique_ptr<float[]> scores(new float[253]);
if (getuid() != 0) {
cerr << "Must be super-user!\n";
return 1;
}
cout << "hello\n";
Must be super-user!
This code isn’t bothered by early returns or exceptions. They will both
cause the unique_ptr to be destroyed, and its dtor will free the
memory.
Simplicity of Description
Consider this non-RAII code:
double *p;
// … region 1 …
p = new double[100];
// … region 2 …
delete[] p;
// … region 3 …
Describe p
in the various regions.
- uninitialized, indeterminate
- pointing to 100 doubles
- indeterminate; probably a stale pointer to the ex-doubles
Simplicity of Description
Consider this RAII code:
unique_ptr<double[]> p(new double[100]);
// … region 1 …
Describe p
in the various regions.
- pointing to 100 doubles
Gosh, that seems easier to understand.
Other Uses
Use RAII when you think “Don’t forget to …”, which might apply to:
- dynamic memory
- files
- mutex
- network sockets
- opening a database
Rule of Zero
Remember the Rule of Three? There’s also the Rule of Zero,
which says, essentially:
- Writing methods is for chumps.
- For anything that requires manual copying or cleanup (dynamic memory,
files), use RAII components (vector, shared_ptr, ifstream).
Their methods can do the manual copying and cleanup.
- Be happy with the compiler-generated copy ctor,
assignment operator, and dtor.
- The best way to ensure bug-free code is to not write any code.
Real-Life RAII
- I treat my teaching assistants as a form of real-life RAII.
- When I need a lab graded, I don’t give them step-by-step
instructions:
- On Friday, tell the TA to download the submissions from Canvas.
- On Saturday, tell the TA to grade the labs.
- On Sunday, tell the TA to enter the scores into Canvas.
- No! I say, “The labs are ready. Do it!” and it gets done, no matter
how often the TA gets interrupted by their life.
- A TA knows how to grade labs.
- A vector knows how to allocate & free memory.
Real-Life RAII