I\'m not talking about pointers to const values, but const pointers themselves.
I\'m learning C and C++ beyond the very basic stuff and just until today I realized t
An example of where a const pointer is highly applicable can be demonstrated thusly. Consider you have a class with a dynamic array inside it, and you want to pass the user access to the array but without granting them the rights to change the pointer. Consider:
#include <new>
#include <string.h>
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const GetArray(){ return Array; }
};
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
}
Which produces:
Input data
put data
But if we try this:
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' ';
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}
We get:
error: lvalue required as left operand of assignment //Drat foiled again!
So clearly we can modify the array's contents, but not the array's pointer. Good if you want to make sure the pointer has a consistent state when passing it back to the user. There is one catch, though:
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' ';
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
delete [] Temp.GetArray(); //Bwuahaha this actually works!
}
We can still delete the pointer's memory reference, even if we can't modify the pointer itself.
So if you want the memory reference to always point to something (IE never be modified, similar to how a reference currently works), then it's highly applicable. If you want the user to have full access and modify it, then non-const is for you.
Edit:
After noting okorz001 comment of not being able to assign due to GetArray() being a right-value operand, his comment is entirely correct, but the above still applies if you were to return a reference to the pointer (I suppose I assumed GetArray was referring a reference), for example:
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const &GetArray(){ return Array; } //Note & reference operator
char * &GetNonConstArray(){ return Array; } //Note non-const
};
int main()
{
TestA Temp;
Temp.GetArray() = NULL; //Returns error
Temp.GetNonConstArray() = NULL; //Returns no error
}
Will return in the first resulting in an error:
error: assignment of read-only location 'Temp.TestA::GetArray()'
But the second will occur merrily despite potential consequences on the underneath.
Obviously, the question will be raised 'why would you want to return a reference to a pointer'? There are rare instances where you need to assign memory (or data) directly to the original pointer in question (for example, building your own malloc/free or new/free front-end), but in those instances it's a non-const reference. A reference to a const pointer I've not come across a situation that would warrant it (unless maybe as declared const reference variables rather than return types?).
Consider if we have a function that takes a const pointer (versus one that doesn't):
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const &GetArray(){ return Array; }
void ModifyArrayConst(char * const Data)
{
Data[1]; //This is okay, this refers to Data[1]
Data--; //Produces an error. Don't want to Decrement that.
printf("Const: %c\n",Data[1]);
}
void ModifyArrayNonConst(char * Data)
{
Data--; //Argh noo what are you doing?!
Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
printf("NonConst: %c\n",Data[1]);
}
};
int main()
{
TestA Temp;
Temp.ModifyArrayNonConst("ABCD");
Temp.ModifyArrayConst("ABCD");
}
The error in the const produces thus message:
error: decrement of read-only parameter 'Data'
Which is good as we probably don't want to do that, unless we want to cause the problems denoted in the comments. If we edit out the decrement in the const function, the following occurs:
NonConst: A
Const: B
Clearly, even though A is 'Data[1]', it's being treated as 'Data[0]' because the NonConst pointer permitted the decrement operation. With the const implemented, as another person writes, we catch the potential bug before it occurs.
One other main consideration, is that a const pointer can be used as a pseudo reference, in that the thing the reference points to cannot be changed (one wonders, if perhaps this was how it was implemented). Consider:
int main()
{
int A = 10;
int * const B = &A;
*B = 20; //This is permitted
printf("%d\n",A);
B = NULL; //This produces an error
}
When attempting to compile, produces the following error:
error: assignment of read-only variable 'B'
Which is probably a bad thing if a constant reference to A was wanted. If B = NULL
is commented out, the compiler will happily let us modify *B
and therefore A. This might not seem useful with ints, but consider if you had a single stance of a graphical application where you wanted an unmodifiable pointer that referred to it that you could pass around.
It's usage is variable (excuse the unintended pun), but used correctly, it is another tool in the box to assist with programming.
const
is a tool which you should use in pursuit of a very important C++ concept:
Find bugs at compile-time, rather than run-time, by getting the compiler to enforce what you mean.
Even though it doesn't change the functionality, adding const
generates a compiler error when you're doing things you didn't mean to do. Imagine the following typo:
void foo(int* ptr)
{
ptr = 0;// oops, I meant *ptr = 0
}
If you use int* const
, this would generate a compiler error because you're changing the value to ptr
. Adding restrictions via syntax is a good thing in general. Just don't take it too far -- the example you gave is a case where most people don't bother using const
.
There's nothing special about pointers where you would never want them to be const. Just as you can have class member constant int
values, you can also have constant pointers for similar reasons: You want to make sure that no one ever changes what's being pointed to. C++ references somewhat address this, but the pointer behavior is inherited from C.
You're right, for the caller it makes absolutely no difference. But for the writer of the function it can be a safety net "okay, I need to make sure I don't make this point to the wrong thing". Not very useful but not useless either.
It's basically the same as having an int const the_answer = 42
in your program.
If you do embedded systems or device driver programming where you have memory mapped devices then both forms of 'const' are often used, one to prevent the pointer from being reassigned (since it points to a fixed hardware address.) and, if the peripheral register it points to is a read-only hardware register then another const will detect a lot of errors at compile time rather than runtime.
A read-only 16 bit peripheral chip register might look something like:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Then you can easily read the hardware register without having to resort to assembly language:
input_word = *peripheral;