C++ Succinctly: Storage Duration

Introduction

To quote the C++ Language Standard, “Storage duration is the property of an object that defines the minimum potential lifetime of the storage containing the object.” Basically, it’s what tells you how long you should expect a variable to be usable. The variable might be a fundamental type, such as an int, or a complex type, such as a class. Regardless of its type, a variable is only guaranteed to last for as long as the programming language says it should.

C++ manages memory very differently from C#. For one thing, there is no requirement to have a garbage collector, and few implementations provide one. To the extent that C++ implementations do have automatic memory management, they mostly do so through smart pointers and reference counting. C++ classes do not automatically live on a heap (GC-managed or otherwise). Instead, they work much more like structures in C#.

You can push a C++ class instance onto a heap when you need to, but if you declare it locally and don’t do anything funny, then it will have an automatic duration, typically implemented using a stack, and will be automatically destroyed when the program leaves the scope in which the class exists.

C++ gives you more control over memory management than C#. A consequence of this is that the C++ language and runtime environment cannot do as much to prevent erroneous code as the C# language and the CLR can. One of the keys to being a good C++ programmer is to understand how memory management works and to use the best practices in order to write efficient, correct code.


Static Duration

Global variables, including those inside namespaces, and variables marked with the duration keyword static have static storage duration.

Global variables are initialized during program initialization (i.e. the period before the program actually starts execution of your main or wmain function). They are initialized in the order in which they are defined in the source code. It’s generally not a good idea to rely on initialization order since refactoring and other seemingly innocent changes could easily introduce a potential bug into your program.

Local statics are zero-initialized the first time program execution reaches the block containing the local static. Typically, they will be initialized to their specified values or initialized by calling the specified constructor at that point. The value assignment or construction phase is not required until the program reaches and executes the statement, except in very rare circumstances. Once a local static is initialized, the initialization specified with its declaration will never run again. This, of course, is just what we would expect of a local static. If it kept initializing itself every time the program reached its definition line, then it would be the same as a non-static local.

You can assign other values to global and local statics, unless you also make them const, of course.


Automatic Duration

Within a block, an object has automatic duration if it is defined without the new operator to instantiate it, and without a storage duration keyword, although it can optionally have the register keyword. This means the object is created at the point when it is defined and is destroyed when the program exits the block its variable was declared in, or when a new value is assigned to its variable.

Note: The auto keyword used to be a way of explicitly selecting automatic storage duration. In C++11, that usage was removed. It now is the equivalent of the var keyword in C#. If you try to compile something using the old meaning of auto, you'll receive a compiler error since auto as a type specifier must be the only type specifier.


Dynamic Duration

Dynamic duration is the result of using either the new operator or the new[] operator. The new operator is used to allocate single objects, while the new[] operator is used to allocate dynamic arrays. You must keep track of the size of a dynamically allocated array. While the C++ implementation will properly free a dynamically allocated array, provided you use the delete[] operator, there is no easy or portable way to determine the size of that allocation. It will likely be impossible. Single objects are freed with the delete operator.

When you allocate memory using new or new[], the return value is a pointer. A pointer is a variable that holds a memory address. In C#, if you set all your references to an object to null or some other value, then the memory is no longer reachable in your program, so the GC can free that memory for other uses.

In C++, if you set all of your pointers to an object to nullptr or some other value, and you cannot figure out the original address using pointer arithmetic, then you have lost your ability to release that memory using the delete or delete[] operators. You have thereby created a memory leak. If a program leaks enough memory, eventually it will crash because the system will run out of memory addresses for it. Even before that, though, the computer will slow horribly, as it is forced to increase paging to accommodate the ever-increasing memory footprint of your program (assuming it has virtual memory, which is absent from most smart phones).

Note: A const pointer, such as someStr in the statement const wchar_t* someStr = L"Hello World!"; is not a dynamic duration pointer. That memory is just part of the program itself. If you try to call delete or delete[] on it, the program will simply crash. A string is an array of characters, however, so if it were okay to delete it, then the delete[] operator would be the correct one to use.

When dealing with dynamic memory, to eliminate potential leaks and limit the possibility of other related bugs, you should always use a smart pointer such as std::unique_ptr or std::shared_ptr. We will discuss these in the chapter that covers pointers.


Thread Duration

Thread duration is the least commonly used storage duration. It has only recently been standardized. As of this writing, few, if any, C++ compiler vendors have implemented support for the new thread_local keyword from the C++11 standard.

This is certain to change, but for now you can use vendor-specific extensions such as the Microsoft-specific extension _declspec(thread) or the GCC-specific extension __thread if you need functionality of this sort.

Thread duration is similar to static duration except instead of lasting the life of the program, these variables are local to each thread; the thread's copy exists for the duration of the thread. Each thread's instance of a thread duration object is initialized when it is first used in the thread, and it is destroyed when the thread exits. A thread duration object does not inherit its value from the thread that started the thread it exists in.


Choosing the Right Storage Duration

Automatic storage duration is usually the right form of storage duration for objects, unless you need them to survive the scope they were created in. If that is the case, you should pick one of the remaining storage durations that best fits your needs.

  • If the object should exist for the whole length of the program’s execution, use static storage duration.
  • If the object should exist for the whole length of a particular thread, use thread storage duration.
  • If the object will only exist for part of the program or thread’s duration, use dynamic storage duration.

You can deviate from those recommendations if doing so makes sense, but in most cases, this guidance will steer you correctly.


Storage Duration Sample

The following sample is included to help clarify these concepts. The sample is heavily documented, so no additional commentary is included. I strongly encourage you to build and run this particular sample. Seeing the output while stepping through the code will help you grasp these concepts more easily than simply reading the code.

Sample: StorageDurationSample\SomeClass.h

Sample: StorageDurationSample\SomeClass.cpp

Sample: StorageDurationSample\StorageDurationSample.cpp

For whom it is inconvenient to run the sample, here is the output I get when I run this from a command prompt on Windows 8 Release Preview, compiled with Visual Studio 2012 Ultimate RC in Debug configuration targeting the x86 chipset. You will probably produce different values for the addresses and thread IDs if you run it on your own system.

Conclusiong

Storage duration is another important aspect of C++ so make sure that you have a good grasp of what we discussed in this article before moving on. Next up are constructors, destructors, and operators.

This lesson represents a chapter from C++ Succinctly, a free eBook from the team at Syncfusion.
Tags:

Comments

Related Articles