Resource Management in C++

Resources

Resources are finite

What is a resource?

  • Memory
  • Files
  • Sockets
  • Threads
  • ...

Garbage collection

Garbage collection works only for memory. It does not work for other resources because it is generally not deterministic.

All GC languages have (some got at a later point) language constructs to allow deterministic resource management.

Python

with open('file.txt') as file:
    for line in file:
        pass
        

C#

// `IDisposable` interface

using (Font font1 = new Font("Arial", 10.0f))
{
    byte charset = font1.GdiCharSet;
}

Go

func Contents(filename string) (string, error) {
    f, err := os.Open(filename)
    if err != nil {
        return "", err
    }
    defer f.Close()  // f.Close will run when we're finished.
    
    

Lifetime of an object

  • starts with completion of the constructor
  • ends with the completion of the destructor

Automatic variables

Automatic variable is allocated and deallocated when the program flow enters and leaves the variable's context.

Variable's context is the scope of its declaration

Scope of a variable

void function() {
    std::ifstream f("input.txt");
} // f is destroyed here
void function() {
    // ...
    {
        std::ifstream f("input.txt");
    } // f is destroyed here
    // ...
}

// loading ...

// loading ...

void function()
{
    if (int x = rand()) {
        std::cout << x << std::endl;
    } else {
        std::cout << "random 0" << std::endl;
    } // x is destroyed here
}
class Logger
{
    private:
        std::ifstream input;
};
// input gets created when a Logger instance gets created
// input gets destroyed when the instance is destroyed
class Logger
{
    private:
        static std::ifstream input;
};
// input gets created sometime before executing main
// input gets destroyed sometime after main has finished
int main() {
}
std::ofstream input("input.txt");
// input gets created sometime before executing main
// input gets destroyed sometime after main has finished
void function() {
    static std::ofstream input("input.txt");
    // input gets created first time executing the function
}
// input gets destroyed sometime after main has finished

non-automatic variables

// ...
std::ifstream* input = new std::ifstream("input.txt");
// ...
delete input; // <-- input gets destroyed here

Automatic variables are cool

  • deterministic* lifetime
  • always* get destroyed
  • * - non-static automatic variables

RAII

Resource Acquisition is Initialization

  • Obtain the resource in the constructor of an automatic variable
  • Free the resource in the destructor of an automatic variable

// loading ...

Lifetime:       0x7fffc7c7552f: lives
Lifetime:       0x7fffc7c7552f: dies

// loading ...

Lifetime:       0x7fffc7c7552e: lives
NeverMember:    0x7fffc7c7552e: is about to live
Lifetime:       0x7fffc7c7552e: dies
exception:not this time

// loading ...

Lifetime:       0x7fffc7c7552d: lives
NeverMember:    0x7fffc7c7552d: is about to live
Lifetime:       0x7fffc7c7552d: dies
exception:not this time

Lifetime of a local reference

// loading ...

Lifetime:       0x7fffcbf05d67: lives
Reference to 0x7fffcbf05d67
Lifetime:       0x7fffcbf05d67: dies

Leaking

// loading ...

Lifetime:       0x119d010:      lives
Leaking:        0x7fff49285b00: lives
exception: it all goes wrong
Resource management is difficult not only in presence of exceptions
bool HasEmbededNull(const char* file)
{
    std::ifstream input(file, std::ios::binary);
    while (input) {
        if (input.get() == '\0') {
            return true;
        }
    }
    return false;
}
bool HasEmbededNull(const char* file)
{
    auto input = std::fopen(file, "rb");
    while (input && !feof(input)) {
        if (fgetc(input) == '\0') {
            return true;
        }
    }
    fclose(input);
    return false;
}
bool HasEmbededNull(const char* file)
{
    auto input = std::fopen(file, "rb");
    while (input && !feof(input)) {
        if (fgetc(input) == '\0') {
            fclose(input); // !!!!!!!!!!!
            return true;
        }
    }
    fclose(input);
    return false;
}

So the moral is?

ALWAYS manage resources following RAII!

NEVER STORE a resource by a plain pointer!