Showing posts with label Const correctness. Show all posts
Showing posts with label Const correctness. Show all posts

Using constants with functions

There always seems to be confusion in using const with functions. I am not referring to const functions that have const after the function name, that is straightforward. Also, the way functions behave when you have const before them is different than the built-in types. I have earlier provided a table for that.

Here is a simple program that shows different types of const with functions.



//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
#include<iostream>
#include<string>

using namespace
std;

class
A
{

public
:
int
x;
};


class
B
{

public
:
string y;
};


A* const createA()
{

return
(new A);
}


const
B* createB()
{

return
(new B);
}


int
main()
{

A* a = NULL;
a = createA();
a->x = 100;
cout<<"a.x = "<<a->x<<endl;
delete
a;

const
B* b = NULL;
b = createB();
//b->y = "Hello"; - Compilation error because b is constant
(const_cast<B*>(b))->y = "Hello";
cout<<"b.y = "<<b->y<<endl;
delete
b;

A* const a2 = NULL;
//a2 = createA(); - Compilation Error:
// you cannot assign to a variable that is const

A* const a3 = new A; //not possible to change what a3 points to now
a3->x = 23456;
cout<<"a3.x = "<<a3->x<<endl;
delete
a3;

return
0;
}






The output is as follows:

Changing objects in 'const member functions' via Mutable

We saw last time an example of 'Const Member Functions' (also known as 'Inspectors'). In contrast the member functions without the const suffix are known as 'non-const member functions' or 'mutators'.

Sometimes it may be necessary to modify a data member (variable or object in your class) in a const member function. There are two approaches to do that. One is to use the const_cast and the other to use 'mutable'. I use both the approaches in the example below:



//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//This example shows how to change objects in const member functions
#include<iostream>

using namespace
std;

class
ABC
{

public
:
int
func1(int a, int b);
int
func2(int a, int b) const;
private
:
int
x;
mutable
int y;
};


int
ABC::func1(int a, int b)
{

x = a, y = b;
cout<<"x = "<<x<<" and y = "<<y<<endl;
return
0;
}


int
ABC::func2(int a, int b) const
{

//x = a; - COMPILE ERROR because its const
int *temp = const_cast<int*>(&x); //Removing the const
*temp = a; //OK now
y = b; //OK because its defined as mutable
cout<<"x = "<<x<<" and y = "<<y<<endl;
return
0;
}


int
main()
{

ABC abc;
abc.func1(3, 7);
abc.func2(20, 40);
return
0;
}



The output is as follows:


Const Pointer and References Table

Interesting table courtesy of my colleague Simon Locke about different possibilities of using const along with pointers and references.

Definition

Read-only

Ownership passed

Allows NULL

Type&NoNoNo
const Type&YesNoNo
Type*NoYesYes
const Type*YesYesYes
Type* constNoNoYes
const Type* constYesNoYes

The definition field is the parameter being passed to a function or a return from the function. For example you can see an example here of the first definition of 'Type&'.

Const after a function name

Here is a classic example of putting const after a function name. What this means is that this function will not modify any private members of this class.




//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//This example shows what happens if const is put after a function
#include<iostream>

using namespace
std;

class
ABC
{

public
:
int
func1(int a, int b);
int
func2(int a, int b) const;
private
:
int
x,y;
};


int
ABC::func1(int a, int b)
{

x = a, y = b;
cout<<"x = "<<x<<" and y = "<<y<<endl;
return
0;
}


int
ABC::func2(int a, int b) const
{

//x = a, y = b; - NOT POSSIBLE, Compile Error
cout<<"Cant change x and y"<<endl;
return
-1;
}


int
main()
{

ABC abc;
abc.func1(3, 7);
abc.func2(20, 40);
return
0;
}


The output is as follows:


Example of 'pointer to const' and 'const pointer'

Continuing with the const correctness theme:


//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Example of pointer to const and const pointer
#include<iostream>

using namespace
std;

int
main()
{

//Lets look at const with pointers
int a = 34;
int
b = 2009;

int
*const x = &a; //COMPULSARY: const object must be initialized
cout<<"*x(1) = "<<*x<<endl;

*
x = 33;
cout<<"*x(2) = "<<*x<<endl;

//x = &b; - NOT POSSIBLE because x is const

const
int *y;
y = &b;
cout<<"*y(1) = "<<*y<<endl;

//*y = 2004; - NOT POSSIBLE
y = x; // POSSIBLE
cout<<"*y(2) = "<<*y<<endl;

int
c = 23456;
int
const *z=&c;
cout<<"*z(1) = "<<*z<<endl;
//*z = 77777; - NOT POSSIBLE, same as *y case
z = x; //POSSIBLE
cout<<"*z(2) = "<<*z<<endl;

//Now lets look const variables
const int i = 1111;
int
const j = 4444;
//i = 66; - NOT POSSIBLE
//j = 77; - NOT POSSIBLE, exactly same as i
//But we can manipulate i or j using pointers and const_cast
int *k = const_cast<int *>(&i);
cout<<"*k(1) = "<<*k<<endl;
*
k = 5678;
cout<<"*k(2) = "<<*k<<endl;
cout<<"i = "<<i<<endl; //Should be 5678 but output maybe 1111
//Change i to const volatile int i = 1111; to get the desired result

return
0;
}


The output is as follows:

It may be sometimes confusing to remember is the const is associated with the pointer or the variable. A simpler way to remember is that const is applied to whatever it is near. Another thing is that You have to read pointer declarations right-to-left. So:

  • const Fred* p means "p points to a Fred that is const" — that is, the Fred object can't be changed via p.
  • Fred* const p means "p is a const pointer to a Fred" — that is, you can change the Fred object via p, but you can't change the pointer p itself.
  • const Fred* const p means "p is a const pointer to a const Fred" — that is, you can't change the pointer p itself, nor can you change the Fred object via p.

A simple example of pointer-to-const

What is "const correctness"?

"const correctness" means using the keyword const to prevent const objects from getting mutated.

In this example we show passing by pointer-to-const. In case of pass by pointer-to-const, any attempts to change to the caller's variable within the function would be flagged by the compiler as an error at compile-time. This check is done entirely at compile-time: there is no run-time space or speed cost for the const. Also passing by pointer-to-const is more efficient as it avoids temporary objects.

So here is the example:


//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Simple program to show const pointers
#include<iostream>

using namespace
std;

class
A
{

public
:
int
*var;
};


void
someFunc(const A* a)
{

//int b=200;
//a = &b; - Not Possible to convert int* to const int*
// We can use a const_cast but thats not the purpose here
*(a->var) = 200; //Possible to change value of const pointer
}

void
anotherFunc(const A* a)
{

if
(a)
{

if
(a->var)
delete
a->var;
delete
a;
}
}


int
main()
{

A *a = new A;
a->var = new int(1024);
cout<<"1: a->var = "<<*(a->var)<<endl;
someFunc(a);
cout<<"2: a->var = "<<*(a->var)<<endl;
anotherFunc(a);
//cout<<"3: a->var = "<<*(a->var)<<endl; - Not Valid
return 0;
}
 


The output is as follows:

Check out this stream