Some time is needed to create an object. Some time is needed to kill an object. When talking about an object, two things are involved: the location which is the storage, and the value. The meaning of lifetime and storage duration are similar; but the duration is seen more from the point of view of the location than from the point of view of the value. The storage duration is the time from when a location is associated to an object to the time when the location is dissociated from the object.
The rest of this article illustrates the object lifetime, and briefly explains the different storage durations. You should have basic knowledge in C++ in order to understand this article. You should also have knowledge in C++ scope.
Article Content
- Illustration of Object Lifetime
- Storage Duration
- Automatic Storage Duration
- Dynamic Storage Duration
- Static Storage Duration
- Thread Storage Duration
- Conclusion
Illustration of Object Lifetime
Consider the following program:
using namespace std;
int main()
{
if (1 == 1)
{
int x;
x = 1;
char y;
y = 'A';
cout << x << y << '\n';
}
return 0;
}
The output is, 1A .
The life of an object comes to an end, when it goes out of scope. The lifetime of object x, begins at “x = 1;” and ends at the end of the if-local-scope. The lifetime of object y, begins at “y = 'A';” and ends at the end of the if-local-scope. Before both object die, they are employed in the cout statement .
Storage Duration
Storage duration is determined by one of the following schemes: automatic storage duration; dynamic storage duration; static storage duration; thread storage duration. Storage duration categories, also apply to references.
Automatic Storage Duration
If a variable, is not declared explicitly as static, thread_local, or extern, then that variable has automatic storage duration. Examples are x and y above. The duration of such variables end when they go out of scope. The following program illustrates automatic storage duration for a reference and a pointer, in the global scope.
using namespace std;
int x = 1;
int& m = x;
char y = 'A';
char* n = &y;
int main()
{
cout << m << *n << '\n';
return 0;
}
The output is, 1A .
The duration of m starts from “int& m = x;” and ends at the end of the program. The duration of n starts from “char* n = &y;” and ends at the end of the program.
Dynamic Storage Duration
Free Store
In a modern computer, more than one program can be running at the same time. Each program has its own portion of memory. The rest of the memory that is not being used by any program, is known as free store. The following expression is used to return a location for an integer from free store
This location (storage) for the integer, returned, still has to be identified by assignment to a pointer. The following code illustrates how to use the pointer with free store:
*ptrInt = 12;
cout<< *ptrInt <<'\n';
The output is 12 .
To put an end to the life of the object, use the delete expression as follows:
The argument for the delete expression, is a pointer. The following code illustrates its use:
*ptrInt = 12;
delete ptrInt;
A pointer created with the new expression and deleted with the delete expression, is of dynamic storage duration. This pointer dies as it goes out of scope, or is deleted. The duration of the object in the previous code, starts at “*ptrInt = 12;” and ends at the end of the declarative region (scope). There is more to the new and delete expressions than has been discussed here – see later.
Static Storage Duration
Static Object
An object declared static, behaves like the ordinary object, except that its storage duration, begins from when it is initialized to the end of the program. It cannot be seen outside its scope, but it can indirectly be employed from outside its scope.
Consider the following program, which is supposed to count from 1 to 5 (do not test the program) :
using namespace std;
int fn()
{
int stc = 1;
cout << ' ' << stc;
stc = stc + 1;
if (stc > 5)
return 0;
fn();
}
int main()
{
fn();
return 0;
}
The output is 1 1 1 1 1 1 1 1 . . . and never really ending. The function definition is a recurring function; meaning it keeps calling itself until a condition is met.
The solution is to make the stc object static. Once a static object has been initialized, its value cannot be changed, until the program ends. The following program (which you can test), which is the same as the above, but now with stc made static, counts from 1 to 5 :
using namespace std;
int fn()
{
static int stc = 1;
cout << ' ' << stc;
stc = stc + 1;
if (stc > 5)
return 0;
fn();
}
int main()
{
fn();
return 0;
}
The output is: 1 2 3 4 5 .
Note: The duration of a static object begins when the object has been initialized, and ends at the end of the program. In the meantime, the object can be used indirectly, from a different scope. Once a static object has been initialized, its initial value cannot be changed, even if its definition is re-evaluated. In the above code, the stc is not reset, the next time it is called. The next time it is called, it is incremented by “stc = stc + 1;”.
Static Data Member
A set of related variables and function can be put in a generalized unit called a class. If the variables are given particular values, the class becomes an object. However, an object is not created by just assigning values to the variable. The class is instantiated to obtain an object; and each object created has its own name different from other objects of the same class. The following program shows a class, called TheCla and an object, called obj; it also shows how the object is instantiated and used in the main() function:
using namespace std;
class TheCla
{
public:
int num;
void func (char cha, const char *str) {
cout << "There are " <<
num << " books worth " <<
cha << str << " in the store." << '\n';
}
};
int main()
{
TheCla obj;
obj.num = 12;
obj.func('$', "500");
return 0;
}
The output is:
Notice, that in order to assign the value of 12 to the variable num, the object has to be instantiated, before the assignment could take place. It is possible for the programmer to assign the value without instantiating (creating) an object. In order to achieve this, the variable, num will have to be declared as static. Then it will be accessed as “TheCla::num” without the object name, but with the class name. The following program illustrates this:
using namespace std;
class TheCla
{
public:
static const int num = 12;
void func (char cha, const char *str) {
cout << "There are " << num <<
" books worth " << cha << str <<
" in the store." << '\n';
}
};
int main()
{
cout << TheCla::num << '\n';
TheCla obj;
obj.func('$', "500");
return 0;
}
The output is:
There are 12 books worth $500 in the store.
Note that in order to access the data member, num in main(), the scope resolution operator, :: had to be used. Also not that the variable, num had to be made constant and initialized in the class description (definition).
Static Member Function
Notice, that in the previous program listing above, in order to use the func function in main(), an object had to be instantiated. It is possible for the programmer to call the function without instantiating (creating) an object. In order to achieve this, the function definition has to be preceded with the word “static”. Then it will be accessed as “TheCla::func()” without the object name, but with the class name. The following program illustrates this for static data member and static member function:
using namespace std;
class TheCla
{
public:
static const int num = 12;
static void func (char cha, const char *str) {
cout << "There are " << num <<
" books worth " << cha << str <<
" in the store." << '\n';
}
};
int main()
{
TheCla::func('$', "500");
return 0;
}
The output is:
Thread Storage Duration
Thread as a feature in C++, has not yet been implemented by the g++ compiler. So, instead of explaining this, the quotation from the C++ specification is given as follows:
- All variables declared with the thread_local keyword have thread storage duration. The storage for these entities shall last for the duration of the thread in which they are created. There is a distinct object or reference per thread, and use of the declared name refers to the entity associated with the current thread.
- A variable with thread storage duration shall be initialized before its first odr-use and, if constructed, shall be destroyed on thread exit.”
Conclusion
The lifetime of an object begins when its initialization is complete, and ends when its storage is released. Dynamic storage duration starts when the storage created by (new Type) is initialized, and ends when the object goes out of scope or is deleted by “delete pointer”. The duration of a static object begins when the object has been initialized, and ends at the end of the program. Once a static object has been initialized, its initial value cannot be changed, even if its definition is re-evaluated. Static data members and static function members are accessed outside the class description with “ClassName::name”.