21. Scope and Lifetime – Modern C++ for Absolute Beginners: A Friendly Introduction to C++ Programming Language and C++11 to C++20 Standards

© Slobodan Dmitrović 2020
S. DmitrovićModern C++ for Absolute Beginnershttps://doi.org/10.1007/978-1-4842-6047-0_21

21. Scope and Lifetime

Slobodan Dmitrović1 
(1)
Belgrade, Serbia
 

When we declare a variable, its name is valid only inside some sections of the source code. And that section (part, portion, region) of the source code is called scope. It is the region of code in which the name can be accessed. There are different scopes:

21.1 Local Scope

When we declare a name inside a function, that name has a local scope. Its scope starts from the point of declaration till the end of the function block marked with }.

Example:
void myfunction()
{
    int x = 123; // Here begins the x's scope
} // and here it ends

Our variable x is declared inside a myfunction() body, and it has a local scope. We say that name x is local to myfunction(). It exists (can be accessed) only inside the function's scope and nowhere else.

21.2 Block Scope

The block-scope is a section of code marked by a block of code starting with { and ending with }. Example:
int main()
{
    int x = 123; // first x' scope begins here
    {
        int x = 456; // redefinition of x, second x' scope begins here
    } // block ends, second x' scope ends here
      // the first x resumes here
} // block ends, scope of first x's ends here

There are other scopes as well, which we will cover later in the book. It is important to introduce the notion of scope at this point to explain the object’s lifetime.

21.3 Lifetime

The lifetime of an object is the time an object spends in memory. The lifetime is determined by a so-called storage duration. There are different kinds of storage durations.

21.4 Automatic Storage Duration

The automatic storage duration is a duration where memory for an object is automatically allocated at the beginning of a block and deallocated when the code block ends. This is also called a stack memory ; objects are allocated on the stack. In this case, the object’s lifetime is determined by its scope. All local objects have this storage duration.

21.5 Dynamic Storage Duration

The dynamic storage duration is a duration where memory for an object is manually allocated and manually deallocated. This kind of storage is often referred to as heap memory . The user determines when the memory for an object will be allocated, and when it will be released. The lifetime of an object is not determined by a scope in which the object was defined. We do it through operator new and smart pointers. In modern C++, we should prefer the smart pointer facilities to operator new.

21.6 Static Storage Duration

When an object declaration is prepended with a static specifier, it means the storage for a static object is allocated when the program starts and deallocated when the program ends. There is only one instance of such objects, and (with a few exceptions) their lifetime ends when a program ends. They are objects we can access at any given time during the execution of a program. We will talk about static specifier and static initialization later in the book.

21.7 Operators new and delete

We can dynamically allocate and deallocate storage for our object and have pointers point to this newly allocated memory.

The operator new allocates space for an object. The object is allocated on the free-store, often called heap or heap memory. The allocated memory must be deallocated using operator delete. It deallocates the memory previously allocated memory with an operator new . Example:
#include <iostream>
int main()
{
    int* p = new int;
    *p = 123;
    std::cout << "The pointed-to value is: " << *p;
    delete p;
}

This example allocates space for one integer on the free-store. Pointer p now points to the newly allocated memory for our integer. We can now assign a value to our newly allocated integer object by dereferencing a pointer. Finally, we free the memory by calling the operator delete.

If we want to allocate memory for an array, we use the operator new[]. To deallocate a memory allocated for an array, we use the operator delete[]. Pointers and arrays are similar and can often be used interchangeably. Pointers can be dereferenced by a subscript operator []. Example:
#include <iostream>
int main()
{
    int* p = new int[3];
    p[0] = 1;
    p[1] = 2;
    p[2] = 3;
    std::cout << "The values are: " << p[0] << ' ' << p[1] << ' ' << p[2];
    delete[] p;
}

This example allocates space for three integers, an array of three integers using operator new[]. Our pointer p now points at the first element in the array. Then, using a subscript operator [], we dereference and assign a value to each array element. Finally, we deallocate the memory using the operator delete[]. Remember: always delete what you new-ed and always delete[] what you new[]-ed.

Remember: prefer smart pointers to operator new. The lifetime of objects allocated on the free-store is not bound by a scope in which the objects were defined. We manually allocate and manually deallocate the memory for our object, thus controlling when the object gets created and when it gets destroyed.