Tuesday, February 5, 2008

Const in C++

Const is great. Seriously it is.

I would never have used const in my pre-professional days (in fact I had never used it before a month ago). What would have been the point? It doesn't really add any functionality or speed etc. But what it does do is try to help you to ensure you don't shoot yourself in the foot, and ensure that other people don't shoot both of you in the foot in some kind of non-const foot shooting massacre.

So, in general const makes a variable or object unchangable (i.e. constant). So something like this will not work:
const int x = 5;
x++; //Error...

You can, of course do something like this:
const int x = 5;
int y = x; //copies value of const variable to a non-const.

y++; //Fine

x++; //Still an error


Const actually has a load of other applications, so I'll try go through them in some sort of logical order (and by that, I mean the order that Scott Meyers does in the highly recommended Effective C++....)

So firstly, in some cases it can be used as a replacement for #defines. This is more of a limitation in using #defines that a huge positive in using consts. The idea that instead of doing something like:
#define PI 3.14
It is possible to do this:
const double pi 3.14;

Why would you want to do this?? Well, the main reason is to stop yourself (and others) getting confused and using PI incorrectly, or more importantly catching times when you use it incorrectly. When you use PI, the value 3.14 is actually replaced in the code before the code is compiled. This means that any error messages will refer to "3.14" rather than PI (as the compiler, which generates the error messages, has no idea what PI is). Any error messages about the const pi will refer to that rather than some mysterious 3.14.

Ok, this is a fairly small reason to use const, and to be honest I never remember getting into too much trouble using a #define for a single value, but I'm prepared to bow down to Scott Mayers advice on this one and so I wanted to include it for completeness. The real meat as far as I'm concerned is still to come.

I'll now talk a little about const when declaring functions. Function declarations can put const in a number of places, as can be seen from the following function that accepts and returns an integer:

int functionName(int x)
const; //(1)
int functionName(
const int x); //(2)
const
int functionName(int x); //(3)

And here is what each of them do:
Number (1) - When declaring a member function, if it is declared in this form then that function cannot change any of the member variables belonging to that class. It is basically like all of the member variables become const for the duration of the function. So the following will not compile:

class SomeClass{
public:

void changeValue(int x)const;

private:

int someValue;

};


void SomeClass::changeValue(int x)const

{

this->someValue = x; //Error - someValue is const for this function

}


This is very useful for member functions like 'getters' which should only read member variables and not write to them. But of course it can be used for any member function that you want to put this restriction on. Of course, careful programming would remove the necessity of this, but for the price of typing 5 letters you get the added security of knowing you won't change any member variables by mistake.

Also, note that using const in this way is only useful on non-static member functions, so using it on a static member function (or a function outside of a class) will result in a compiler error.

Ok, on to Number(2): This says that the function cannot change the value x that is passed to the function cannot be changed. This is useful if you are passing in a value and you want to be sure that it will not be changed by mistake. See the following code:

void someFunction(const int x)

{

int y = x; //Fine - const->non-const copying is fine

x = x + 1; //
Error - cannot assign to a variable that is const
}


Finally, there is Number(3): The first time I saw this I thought - "This must mean that when you return the const int from functionName(), that the return value cannot be changed". But this is wrong, wrong, wrong.... and when I thought about it it makes sense. What is actually happening when you return a variable from a method by value (as we are doing here)? Well, we are essentially copying the value from within the function to the value outside it. And copying a const value to a not const value is fine.

So, in the form that it is on, statement (3) doesn't really do anything. i.e. this is perfectly legal;

const int someFunction()

{

const int x = 5;

return(x);

}

int main(int argc, char* argv[])

{

int y = someFunction();

y++; //fine.

}

This is basically the same as:

const int constInt = 5;
int normalInt = constInt; //Fine, copying the value
normalInt++; //Fine


Where declaring the return type const is only really useful if the return value is going to be directly operated on, i.e. when returning references or pointers. So, for example, if you had a class set up as so:

class SomeClass{

public:

const int& getTimesCalled(){return(SomeClass::numTimesCalled);};

int& getTimesCalledNonConst(){return(SomeClass::numTimesCalled);};

private:

static int numTimesCalled;

};

int SomeClass::numTimesCalled = 0;


With a const and non-const returned reference to numTimesCalled, the following behaviour is observed:

int main(int argc, char* argv[])

{

SomeClass someClass;

int retVal = someClass.getTimesCalled(); //Legal - the value of
numTimesCalled
//gets copied to retVal
retVal++; //Legal, retVal gets incremented by 1, numTimesCalled uneffected.


retVal = someClass.getTimesCalledNonConst(); //Same as above

retVal++; //Same as above


int &refRetVal = someClass.getTimesCalled(); //Illegal, cannot assign to a

//non const reference...


int &refRetVal2 = someClass.getTimesCalledNonConst(); //Legal

refRetVal2 ++; //Legal, but changes the value numTimesCalled (in this

//case undesired)


const int &refRetValConst = someClass.getTimesCalled(); //Legal

refRetValConst ++; //Illegal, changing assign to a variable that is const.

}


So, in this case the const return value is preffered as it stops outsiders changing the numTimesCalled variable.

It also has the added advantage of stopping nasty behaviour like this:

someClass.getTimesCalledNonConst()++; //Whoa, changes
//numTimesCalled,
definitely undesired!!!

someClass.getTimesCalled()++;//Illegal cannot assign to a const (desired)



See I told you const was great!! That's all for now.

--
Lurning Man
--

No comments: