CV stands for Constant-Volatile. The declaration of an object that is not preceded by const and/or volatile is a cv-unqualified type. On the other hand, the declaration of an object that is preceded by const and/or volatile is a cv-qualified type. If an object is declared const, the value in its location cannot be changed. A volatile variable is a variable whose value is under the influence of the programmer, and hence cannot be changed by the compiler.Storage Class Specifiers refer to the life, place, and way in which a type exists. Storage class specifiers are static, mutable, thread_local, and extern.
This article explains C++ Qualifiers and Storage Class Specifiers. Thus, some preliminary knowledge in C++ comes in handy to really appreciate the article.
Article Content:
Qualifiers:
const
An object declared constant is an object the storage (location) of whose value cannot be changed. For example, in the statement:
The value of 5 in the storage for theInt cannot be changed.
volatile
Consider the following statement:
Compilers sometimes interfere with the value of a variable with the hope of optimizing the program. The compiler may maintain the value of a variable as constant when it is not supposed to be constant. Object values that have to do with memory-mapped IO ports, or Interrupt Service Routines of peripheral devices, can be interfered with by the compiler. To prevent such interference, make the variable volatile, like:
portVal = 26904873;
or like:
int volatile portVal = 26904873;
Combining const and volatile:
const and volatile may occur in one statement as follows:
cv-qualifiers
A variable preceded with const and/or volatile is a cv-qualified type. A variable not preceded with either const or volatile or both is a cv-unqualified type.
Ordering:
One type can be more cv-qualified than another:
- No cv-qualifier is less than a const qualifier
- No cv-qualifier is also less than a volatile qualifier
- No cv-qualifier is less than a const-volatile qualifier
- const qualifier is less than a const-volatile qualifier
- volatile qualifier is less than a const-volatile qualifier
It has not yet been concluded if const and volatile are of the same rank.
Array and Instantiated Object:
When an array is declared constant, as in the following statement, it means that the value of each element of the array cannot be changed:
Whether it’s an ‘a’, ‘b’, ‘c’, or ‘d’, it still can’t be changed to some other value (character).
A similar situation is applies to an instantiated object of a class. Consider the following program:
using namespace std;
class Cla
{
public:
char ch0 = 'a';
char ch1 = 'b';
char ch2 = 'c';
char ch3 = 'd';
};
int main()
{
const Cla obj;
return 0;
}
Due to the statement “const Cla obj;” with const in the main() function, neither ‘a’ nor ‘b’ nor ‘c’ nor ‘d’ can be changed to some other value.
Storage Class Specifiers:
Storage class specifiers are static, mutable, thread_local, and extern.
The static Storage Class Specifier
The static storage class specifier allows the variable to live after its scope has gone through, but it cannot be accessed directly.
The following program illustrates this, with a recursive function:
using namespace std;
int funct()
{
static int stac = 10;
cout << stac < 50)
{
cout << '\n';
return 0;
}
funct();
}
int main()
{
funct();
return 0;
}
The output is:
If a static variable is not initialized at its first declaration, it assumes the default value for its type.
The static specifier can also be used with members of a class; the use here is different. Here, it allows the member to be accessed without instantiation for the object.
The following program illustrates this for a data member:
using namespace std;
class Cla
{
public:
static const int num = 8;
};
int main()
{
cout << Cla::num << '\n';
return 0;
}
The output is:
The static data member has to be constant. Note that the use of the scope resolution operator to access the static variable outside its scope (in the main function).
The following program illustrates the use of “static” for a member function:
using namespace std;
class Cla
{
public:
static void method ()
{
cout << "Of static member function!" << '\n';
}
};
int main()
{
Cla::method();
return 0;
}
The output is:
Of static member function!
Note that the use of the scope resolution operator to access the static member function outside its scope (in the main function).
The mutable Specifier
Remember, from above, that if an instantiated object begins with const, the value of any of its normal data members cannot be changed. And for any such data member to be changed, it has to be declared, mutable.
The following program illustrates this:
using namespace std;
class Cla
{
public:
char ch0 = 'a';
char ch1 = 'b';
mutable char ch2 = 'c';
char ch3 = 'd';
};
int main()
{
const Cla obj;
obj.ch2 = 'z';
cout << obj.ch0 << ' ' << obj.ch1 << ' ' << obj.ch2 << ' ' << obj.ch3 << ' ' << '\n';
return 0;
}
The output is:
The thread_local Specifier
In the normal running of a program, one code segment is executed, then the next code segment, followed by another code segment after that, and so on. That is one thread; the main thread. If two code segments execute at the same time (same duration), then a second thread is needed. The result of the second thread may even be ready before the main thread.
The main() function is like the main thread. A program may have more than two threads for such an asynchronous behavior.
The second thread needs a scope (block scope) in order to operate. This is typically provided by the function scope, a function. A variable in an outer scope that can be seen in the scope of the second thread.
The following short program illustrates the use of the thread_local specifier:
#include <thread>
using namespace std;
thread_local int inter = 1;
void thread_function()
{
inter = inter + 1;
cout << inter << "nd thread\n";
}
int main()
{
thread thr(&thread_function); // thr starts running
cout << inter << "st or main thread\n";
thr.join(); // main thread waits for the thread, thr to finish
return 0;
}
The output is:
2nd thread
The variable, inter, preceded by thread_local, means that inter has a separate instance in each thread. And that it can be modified in different threads to have different values. In this program, it is assigned the value, 1 in the main thread, and modified to the value, 2 in the second thread.
A thread needs a special object in order to operate. For this program, the library included by “#include <thread>” has a class called a thread, from which the object thr has been instantiated. The constructor for this object takes a reference to the thread function as an argument. The name of the thread function in this program is thread_function().
The join() member function for the special object, at its position employed, makes the main thread wait for the second thread to finish executing before it continues to execute, otherwise, the main() function may exit without the (second) thread having yielded its result.
The extern Specifier
In simple terms, for a declaration, memory is not allocated for the variable or function, while for a definition, memory is allocated. The extern reserved word allows a global variable or function to be declared in one file but defined in another. Such files are called translation units for the complete C++ application.
Type the following program and save it with the file-name, mainFile:
using namespace std;
int myInt;
const char ch;
void myFn();
int main()
{
myFn();
return 0;
}
The variable, myInt, the constant variable, ch, and the function, myFn(), have been declared without being defined.
Type the following program with the definitions, and save it with the file-name, otherFile, in the same directory:
using namespace std;
int myInt = 10;
const char ch = 'c';
void myFn()
{
cout << "myFn() says " << myInt << " and " << ch <<'\n';
}
Try to compile the application at the terminal (DOS command prompt) with the following command, and notice that it may not compile:
Now, precede the three declarations in mainFile with the word “extern”, as follows:
extern const char ch;
extern void myFn();
Re-save mainFile. Compile the application with:
(This is how separate files for the same application are compiled in C++)
And it should compile. Now, run the application, complete.exe, and the output should be:
Note that with the use of “extern”, a constant variable can be declared in one file but defined in another. When dealing with function declaration and definition in different files, the use of extern is optional.
When to use extern? Use it when you do not have header files with global declarations.
“extern” is also used with template declarations – see later.
Conclusion:
A variable preceded with const and/or volatile is a cv-qualified type. A variable, not preceded with either const or volatile or both, is a cv-unqualified type.
Storage class specifiers are static, mutable, thread_local, and extern. These affect the life span (duration), place, and way of employment of variables in an application.
from Linux Hint https://ift.tt/3b0HbYf
0 Comments