Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

C++ Const, Volatile Type Qualifiers

4.89/5 (4 votes)
23 Nov 2013CPOL5 min read 22K  
This article describes basic concepts of C++ const and volatile type qualifiers.

Introduction

This article describes basic concepts of C++ const and volatile type qualifiers.

Type Qualifiers

  • Type specifiers indicate the type of the object or function being declared.
  • Type Qualifiers add refinement to the type of the object or function being declared.
  • C++ recognizes const and volatile type qualifiers.

Const Type Qualifiers

  • C const qualifier explicitly declares a data object as something that cannot be changed
  • A const object or variable must be initialized and its value is set at initialization.
    C#
     <![CDATA[ class X{ int a; } 
    int main() 
    { 
    const X obj; //no user defined constructor 
    const int f; //not initialized will give error 
    } 

  • In case of class object, all the member variables must be initialized in the constructor. A default user defined constructor will initialize all the values to random value.
    C#
     <![CDATA[ class X{ X(){}; 
    void print(){}; 
    int a; 
    } 
    int main() 
    { 
    const X obj; //used defined constructor present 
    const int f=1; //no error 
    } 
  • When object is defined as constant, no data members of class can be modified, or any non-constant member function cannot be called.
  • You cannot use const data objects in expressions requiring a modifiable lvalue, i.e., left side of assignment.
    C#
     <![CDATA[ class X
    { 
    X(){}; 
    void print(){}; int a; 
    } 
    int main() 
    { 
    const X obj; //used defined constructor present 
    const int f=1; //no error 
    f=2; 
    obj.a=2; 
    obj.print(); 
    } 

  • An object that is declared const is guaranteed to remain constant for its lifetime, not throughout the entire execution of the program. And thus const variables cannot be used in place of constant or in a constant expression. The const variable k is const for the lifetime of the function, it is initialized to value of j at the start of the function and remains a const till the function returns only.
    C#
     <![CDATA[ void foo(int j) 
    { 
    const int k = j; 
    int ary[k]; /* Violates rule that the length of each array 
    must be known to the C compiler, however this is allowed in C++ compiler */ 
    } 
  • A const variable has internal linkage by default, storage qualifiers need to be specified explicitly. Const Objects can be used in header files, since they have internal linkages and can be used instead of \#define for constant values;
  • For a const pointer, you must put the keyword between the * and the identifier. In this case, the value of pointer/address cannot be changed but the value the pointer is pointing to can be changed.
    C#
     <![CDATA[ int c=0; 
    int * const y=&c; 
    int a=10,b=20; 
    *y=a; 
    cout << *y << ":" << y << endl; 
    *y=b; cout << *y << ":" << y << endl; 

    C#
     <![CDATA[ int c=0; 
    int * const y=&c; 
    int a=10,b=20; 
    y=&a; //this line will give compilation error 

  • To define a pointer to a const value, the keyword must be placed before the type specifier.
    C#
     <![CDATA[ int c=0; 
    const int * y=&c; // A pointer to a constant integer 
    int a=10,b=20; y=&a; 
    cout << *y << ":" << &c <<":" << 
    y << endl; y=&b; cout << 
    *y << ":" << &c <<":" << 
    y << endl; 
    a=30; cout << 
    *y << ":" << &c <<":" << 
    y << endl; 

    We can change the values of a,b,c and since in the last line pointer points to address of b, we can deference the pointer and obtain the value of new variable.

  • We can see that the pointer can be changed, however changing the value the pointer y points gives a error. It is a pointer to a const integer.

    Any expression of the form $*y=1$ will give an error. We cannot change the value of the variable via pointer.

    C#
     <![CDATA[ int c=0; 
    const int * y=&c; // A pointer to a constant integer 
    *y=1; //this line gives error 

  • In the below expression, neither the value pointed to or the pointer can be changed
    C#
    <![CDATA[ const int * const ptr ;
    
  • Declaring a member function as const means that the this pointer is a pointer to a const object. \\ There are reasons that you will want the compiler to prevent a member function from being able to change any private member variables of the calling objects. \\
  • The data member of class will be constant within that function.
    C#
     <![CDATA[ class X
    { 
    int x; 
    public: X():x(100){}; 
    void print() const{ x=1; //this gives error } 
    }; 

  • A const member function cannot call any non-const member function.
    C#
     <![CDATA[ class X
    { 
    int x; 
    public: X():x(100),a2(200){a1=&x;}; 
    void print2 (){ } 
    void print() 
    const{ print2();//this gives error 
    } 
    }; 
    int main() 
    { X obj; obj.print(); } 

  • If an object of class is delared as const, it can only call const functions of the class, but not non-const functions of the class. Declaring the object as const means that this pointer is a pointer to a const object.
    C#
     <![CDATA[ class X
    { int x; public: X():x(100){}; 
    void print2 (){ } void print() const{ } }; 
    int main() 
    { const X obj; 
    obj.print(); //this does not give error 
    obj.print2(); //this gives an error } 

  • If a function is defined as const, we can still change the value using const_cast. A better approach would be to declare the variable desired to be changed as mutable.
    C#
     <![CDATA[ class X
    { 
    int x; 
    public: X():x(100){}; 
    void print() const{ const_cast <int>(x)=1; // will not give error } 
    }; 
  • The mutable storage class specifier is used only on a class data member to make it modifiable even though the member is part of an object declared as const or function is declared as const.
    C#
     <![CDATA[ class X
    { mutable int x; 
    public: X():x(100){}; 
    void print() const{ x=1; //this is allowed since x is mutable } 
    int main() { const X obj; obj.print(); } 
    }; 
  • Pointer to constant data can be used as function parameters to prevent the function from modifying a parameter passed through a pointer or passed as a reference.
    C#
     <![CDATA[ class X
    { public: X(){} 
    void print(const int * x2,const int & x1) 
    { *x2=1; //pointer to const data, hence will give error 
    x1=1; //reference to const data, hence will give error } }; 

  • The member function can also return a const value or reference to const value. A return of an object invokes a copy constructor while returning a reference does not invoke a copy constructor. Thus for efficiency it can be used. However the return by reference supplies the local address of a variable, not if the main object has been deallocated, the value which address points to is undefined. \\ The reference should be to an object that exists.
    C#
     <![CDATA[ class X
    { public: X(){} 
    const int &print
    (const int * x2,const int & x1) 
    { int x3=1; 
    cerr << x3 <<":" << &x3 << endl; 
    return x3; 
    } 
    }; 
    int main() 
    { 
    X *obj=new X(); 
    int x=0; 
    const int & 
    x4=obj->print(&x,x); 
    cerr << 
    x4 <<":" << &x4 << endl; 
    delete obj; cerr << x4 <<":" << &x4 << endl; 
    } 

    We can see that value pointed by x4 is difference after the object has been deallocated. This is inherent drawback of call by reference, where user must ensure that the object is not being referenced.

  • However, in case of applications like streaming camera frames, sensor data, etc., we do not want to allocate a new address for every new frame of data. In this case, returning a constant reference or pointer provides an efficient way of providing the data without copying it or allocating it everytime.
    C#
     <![CDATA[ class X
    { 
    int x3; 
    public: X():x3(0){} 
    const int &get() 
    { x3=x3+1; return x3; } 
    }; 
    int main() 
    { 
    X *obj=new X(); 
    int x=0; 
    for(int i=0;i<2;i++) 
    { const int & 
    x4=obj->get(); 
    cerr << x4 <<":" << &x4 << endl; //address of x4 is same as x3 } 
    } 

Volatile Type Qualifier

  • The volatile qualifier maintains consistency of memory access to data objects.
  • Volatile objects are read from memory each time their value is needed, and written back to memory each time they are changed.
  • The volatile qualifier declares a data object that can have its value changed in ways outside the control or detection of the compiler and the compiler is thereby notified not to apply certain optimizations to code referring to the object.
  • When applied to a class, all members of the class have the same type qualifiers.
  • An item can be both volatile and const. In this case, the item is modified by some asynchronous process.
  • You can define or declare any function to return a pointer to a volatile or const function.
  • You can declare or define a volatile or const function only if it is a nonstatic member function.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)