Skip to main content

Constant members of a Class

A constant  is a value which can not be modified. As in C, we can have literal constants using #define and we can have enums and we can define a variable to be const.  Let us look at const variables here.

By declaring a variable as const, we ensure that it is not modified accidentally. Any modification to a constant will give a compilation error.

A const should always be initialized while defining.

In the program below,  assignment to pi gives a compiler error because pi is defined as const and code is trying to modify this.


int main()
{
 const float pi = 22.0/7;
 int radius = 12;
 radius++;/*ok*/
 pi = 3.14;/*error*/
}

 Constant parameters to functions

Even function parameters can be constant. We have earlier discussed that making a reference parameter as constant will avoid the function from accidentally modifying the argument.

void printnum(int & n)
{
 cout<<n++;
}
void printnum2(const int & n)
{
 cout<<n++;/*error*/
}
int main()
{
 int a = 10;
 printnum(a);
 cout<<a;/*prints 11*/
 printnum2(a);
}

In printnum() function, n++ will increment the parameter which may be unintentional but is not caught by the compiler. But in printnum2(), n++ gives a compiler error, because the parameter is a constant reference, and it can not be modified.

Unless your function needs to change the reference parameter,  make the parameter as a constant reference.

Next let us consider how to define and use constant members of a class.

Constant Data Members of a class

A class can have constant data members by using keyword const in declaration. Such members can not be modified after the object is created.


The question arises - how do we initialize such const members?

class A
{
 const int n;
public:
 A();
};

A::A() 
{
 n= 10;/*error*/
}

Initializing const data members must be done using member initializer list. Because const data can not be assigned even within a constructor body.

class A
{
 const int n;
public:
 A();
};
A::A():n(10)
{
}

As can be seen in the example,  const member n is initialized in member initializer list.
If a class has a const data member
  1. write a constructor for that class
  2. in the constructor, initialize the const member using member initializer list
Constant Objects
 

We can define a constant object. Once defined, the state of this object can not be modified. Its data members can not be modified either directly or using member functions.
class A
{
 int a;
public:
 void print();
 void seta(int m){a = m;}
};
void A::print()
{
 cout<<a;
}
int main()
{
 const A obj1; 
 obj1.seta(12);//error
 obj1.print();//error
}

The object obj1 is a constant object and can not be modified. So obviously it can not call the function seta(). But it can not even call print() function which does not modify data members. Why?

Because compiler allows constant object to call only constant member functions - i.e. functions which can not modify state of the object.

Constant functions


A constant member function is a function which guarantees that it does not modify the state of the object i.e. it can not modify any data member.

A const function is written by adding keyword const after the parameter list. This const key word should be added both in declaration of the function and definition.

class A
{
 int a;
public:
 void print() const;
 void seta(int m){a = m;}
};
void A::print() const
{
 cout<<a;
 // a = 0;/*invalid*/
}
int main()
{
 const A obj1;
 obj1.print();//valid
 //obj1.seta(12);//error
 A obj2;
 obj2.seta(12);//valid
 obj2.print();//valid
}

As you can see in the modified code above, print() function, which is now a const function, is not allowed to change a  - the data member. Trying to modify a in print() function gives a compiler error.

 obj1 which is a constant object can call const function viz. print(). It can not call seta() function because it is not const function.

But a non-constant object
obj2 can call both const and non-const function.

We can overload a function using constantness. That is there can be two member functions which are similar but one is const and other is not const.


class A
{
public:
    void print();
    void print() const;
};




Mutable members:

Even from a constant object, mutable data members can be modified and they can be changed even inside a const functions.

class A
{
 mutable int num_accessed;/*mutable member*/
 int b;
public:
 /*other code*/
 void print() const;
};

void A::print() const
{
 cout<<num_accessed<<" "<<b;
 num_accessed++;/* valid. the member is mutable*/
}


In the code above, num_accessed is incremented within a const function and the program would compile without errors.

Comments

Popular posts from this blog

Polymorphism

You hear the term Polymorphism too frequently with object oriented languages. Along with Inheritance and Encapsulation, polymorphism is one of the corner stones of object oriented design. What is Polymorphism, exactly? Polymorphism is a mechanism by which you provide single interface for multiple methods. (poly - many, morph - form). In C++, polymorphism can be compile time or run-time. Compile time polymorphism is provided with the help of overloaded operators/functions and templates. Run time polymorphism is provided with the help of virtual functions and late binding. Late Binding: Connecting a function call to function body is called binding. Most functions use early binding where this binding happens before the program is run - during compile time. This is also called static binding. Late binding (also called dynamic binding)  is when a function call is connected to function body at run time. This is done after looking at the type of the object. Late binding is ach

Operator Overloading

What is operator overloading ? Operator overloading is the process of customizing C++ operators for operands of user defined types.   When you have two objects of a class- num1 and num2 , you can write a function to add them such as  ans = add(num1,num2); That does not look neither simple nor intuitive. You would prefer to write      ans = num1+num2; as you would write expressions for basic data types like integers, floats etc.    This can be done using Operator overloading. Operator overloading lets you write such statements. That is, it lets you call your functions on objects using  +, - ,* etc.    + operator will call addition function on the object (when you write op. overloading function for +). * will call multiply on objects etc. Names of overloaded operator functions start with keyword operator followed by  symbol of the operator. e.g. +, - etc. Unary operator functions take 0 parameters for members. The operand for these function is the object cal