In computer science, const-correctness is the form of program correctness that deals with the proper declaration of objects as mutable or immutable. The term is mostly used in a C or C++ context, and takes its name from the const keyword in those languages. The idea of const-ness does not imply that the variable as it is stored in the computer's memory is unwriteable. Rather, const-ness is a compile-time construct that indicates what a programmer may do, not necessarily what he or she can do. In addition, a class method can be declared as const, indicating that calling that method does not change the object. Such const methods can only call other const methods but cannot assign member variables. (In C++, a member variable can be declared as mutable, indicating that a const method can change its value. Mutable member variables can be used for caching and reference counting, where the logical meaning of the object is unchanged, but the object is not physically constant since its bitwise representation may change.) In C++, all data types, including those defined by the user, can be declared const, and all objects should be unless they need to be modified. Such proactive use of const makes values "easier to understand, track, and reason about," and thus, it increases the readability and comprehensibility of code and makes working in teams and maintaining code simpler because it communicates something about a value's intended use. For simple data types, applying the const qualifier is straightforward. It can go on either side of the type for historical reasons (that is, const char foo = 'a'; is equivalent to char const foo = 'a';). On some implementations, using const on both sides of the type (for instance, const char const) generates a warning but not an error. For pointer and reference types, the syntax is slightly more subtle. A pointer object can be declared as a const pointer or a pointer to a const object (or both). A const pointer cannot be reassigned to point to a different object from the one it is initially assigned, but it can be used to modify the object that it points to (called the "pointee"). (Reference variables are thus an alternate syntax for const pointers.) A pointer to a const object, on the other hand, can be reassigned to point to another object of the same type or of a convertible type, but it cannot be used to modify any object. A const pointer to a const object can also be declared and can neither be used to modify the pointee nor be reassigned to point to another object. The following code illustrates these subtleties: void Foo( int * ptr, int const * ptrToConst, int * const constPtr, int const * const constPtrToConst ) { *ptr = 0; // OK: modifies the pointee ptr = 0; // OK: modifies the pointer *ptrToConst = 0; // Error! Cannot modify the pointee ptrToConst = 0; // OK: modifies the pointer *constPtr = 0; // OK: modifies the pointee constPtr = 0; // Error! Cannot modify the pointer *constPtrToConst = 0; // Error! Cannot modify the pointee constPtrToConst = 0; // Error! Cannot modify the pointer To render the syntax for pointers more comprehensible, a rule of thumb is to read the declaration from right to left. Thus, everything before the star can be identified as the pointee type and everything to after are the pointer properties. (For instance, in our example above, constPtrToConst can be read as a const pointer that refers to a const int.) References follow similar rules. A declaration of a const reference is redundant since references can never be made to refer to another object: int i = 42; int const & refToConst = i; // OK int & const constRef = i; // Error the "const" is redundant Even more complicated declarations can result when using multidimensional arrays and references (or pointers) to pointers. Generally speaking, these should be avoided or replaced with higher level structures because they are confusing and prone to error.
There are four ways to declare a pointer:
<type> * <name>;
- a variable pointer to a variable type.
const <type> * <name>;
- a variable pointer to a constant type.
<type> * const <name> {address};
- a constant pointer to a variable type.
const <type> * const <name> {address};
- a constant pointer to a constant type.
The last two declarations are constant pointers. Being constants, you must assign an address to them at the point of instantiation.
Note that the 2nd declaration is a pointer to a constant, not a constant pointer. That is, the object being referred to is treated as if it were constant but the pointer itself is variable so we can still change the address, but not the value at that address.
int const *p declares a 'p' pointer, that points to a constant integer
Every non-static member function has a hidden pointer parameter named this which refers to the instance of the class the function was invoked against. For a given class, C, the type of the hidden this pointer is const C* but if the function is declared const, the pointer is const C* const. When referring to any class member, m, from within any non-static member function, this->m is implied.
//Array Passer //Demonstrates relationship between pointers and arrays #include <iostream> using namespace std; void increase(int* const array, const int NUM_ELEMENTS); void display(const int* const array, const int NUM_ELEMENTS); int main() { cout << "Creating an array of high scores.\n\n"; const int NUM_SCORES = 3; int highScores[NUM_SCORES] = {5000, 3500, 2700}; cout << "Displaying scores using array name as a constant pointer.\n"; cout << *highScores << endl; cout << *(highScores + 1) << endl; cout << *(highScores + 2) << "\n\n"; cout << "Increasing scores by passing array as a constant pointer.\n\n"; increase(highScores, NUM_SCORES); cout << "Displaying scores by passing array as a constant pointer to a constant.\n"; display(highScores, NUM_SCORES); return 0; } void increase(int* const array, const int NUM_ELEMENTS) { for (int i = 0; i < NUM_ELEMENTS; ++i) array [i] += 500; } void display(const int* const array, const int NUM_ELEMENTS) { for (int i = 0; i < NUM_ELEMENTS; ++i) cout << array[i] << endl; }
An array's name is not a constant pointer; it's not even a pointer! An array name is a reference to the array itself. Unlike pointers, references have no memory of their own, therefore a reference cannot be constant (only what it refers to can be constant). A reference is nothing more than an alias for a memory address. Since there is no separate storage for references, you cannot reassign references while they remain in scope. So, in that sense, it behaves like a constant pointer. But a pointer is a variable, so even if declared const, it requires memory of its own in order to store the memory address it points to.Example:int a[10];int * const p = a; // const pointerassert( *p &a ); would cause an assertion. Likewise, assert( p != &p); proves that p must reside in a separate memory address from that referred to by a. It has to: pointers are variables even when they are declared const.
A constant member function is an instance method that can only modify the mutable members of the class instance. This is achieved by implicitly declaring the this pointer constant. The this pointer is a hidden parameter that is implicitly passed to every instance function, and that points to the current instance of the class. Static member functions do not have an implicit this pointer and therefore cannot be declared const. Consider the following simple class that has both mutable and non-mutable member variables, and both constant and nonconstant member functions: struct foo { void constant()const; void nonconstant(); private: int data1; mutable int data2; }; void foo::constant()const { data1++; // not permitted: the implicit this pointer is declared const data2++; // ok: data2 is mutable } void foo::nonconstant() { data1++; // ok: the implicit this pointer is declared non-const data2++; // ok: data2 is mutable } Note that data2 can always be modified since it is declared mutable, but data1 can only be modified by nonconstant member functions. Members should only be declared mutable when they are only used internally by a class (such as when locking a mutex for thread safety), or where a value needs to be calculated and cached the first time it is accessed.
int const *p declares a 'p' pointer, that points to a constant integer
Every non-static member function has a hidden pointer parameter named this which refers to the instance of the class the function was invoked against. For a given class, C, the type of the hidden this pointer is const C* but if the function is declared const, the pointer is const C* const. When referring to any class member, m, from within any non-static member function, this->m is implied.
Pointer to constant *ptr=10 statement is invalid in pointer to constant i.e assigning value is illegal ptr++ statement is valid in pointer to constant. pointer can be incremented and decremented, Pointer is pointing to constant data object. Declaration: const int *ptr; Constant pointers: *ptr= 10 is absolutely valid in constant pointers i.e assigning value is perfectly legal ptr+++ statement is invalid in constant pointers. pointer can not be incremented or decremented. Declaration; int *const ptr;
Yes. However, making a const pointer seems rather pointless, as you will be able to allocate and deallocate memory for it, but you will be unable to change the contents.
#define NULL ((void *)0) /* defined in <stddef.h> */ const char *mynullvar = NULL;
//Array Passer //Demonstrates relationship between pointers and arrays #include <iostream> using namespace std; void increase(int* const array, const int NUM_ELEMENTS); void display(const int* const array, const int NUM_ELEMENTS); int main() { cout << "Creating an array of high scores.\n\n"; const int NUM_SCORES = 3; int highScores[NUM_SCORES] = {5000, 3500, 2700}; cout << "Displaying scores using array name as a constant pointer.\n"; cout << *highScores << endl; cout << *(highScores + 1) << endl; cout << *(highScores + 2) << "\n\n"; cout << "Increasing scores by passing array as a constant pointer.\n\n"; increase(highScores, NUM_SCORES); cout << "Displaying scores by passing array as a constant pointer to a constant.\n"; display(highScores, NUM_SCORES); return 0; } void increase(int* const array, const int NUM_ELEMENTS) { for (int i = 0; i < NUM_ELEMENTS; ++i) array [i] += 500; } void display(const int* const array, const int NUM_ELEMENTS) { for (int i = 0; i < NUM_ELEMENTS; ++i) cout << array[i] << endl; }
A pointer into an array of elements of type E is a pointer to a single element of type E:typedef ..... E;E array[123];E* const pointer = &array[18]; // points to the 19th element inside 'array'An array of pointers is an array whose elements are pointers:typedef .... E;E* array[123];E** const pointer = &array[18]; // points to the 19th pointer within 'array'Referencing the name of the array variable without use of the index operator itself is a constant pointer to its first element. Therefore, the following if-clause is always true:typedef .... E;E array[123];if (array &array[N]) { // ALWAYS true ...}
The C++ typeid operator returns a const-qualified lvalue object of type std::type_info, as defined in the standard library header.
An array's name is not a constant pointer; it's not even a pointer! An array name is a reference to the array itself. Unlike pointers, references have no memory of their own, therefore a reference cannot be constant (only what it refers to can be constant). A reference is nothing more than an alias for a memory address. Since there is no separate storage for references, you cannot reassign references while they remain in scope. So, in that sense, it behaves like a constant pointer. But a pointer is a variable, so even if declared const, it requires memory of its own in order to store the memory address it points to.Example:int a[10];int * const p = a; // const pointerassert( *p &a ); would cause an assertion. Likewise, assert( p != &p); proves that p must reside in a separate memory address from that referred to by a. It has to: pointers are variables even when they are declared const.
Yes, objects can be passed to functions in C++. The function's parameter determines how the object is handled by the function. The following examples show all the possible parameter variations:By reference:void foo(const MyObject& o);void foo(MyObject& o);By value:void foo(const MyObject o);void foo(MyObject o);By pointer:void foo(MyObject* p);void foo(const MyObject* p);void foo(MyObject* const p);void foo(const MyObject* const p);The pointer variations can be extended to include pointer to pointer variants and so on. However, it is not possible for any one function in the same namespace to be overloaded with all eight of these variations unless the return values are altered or dummy parameters are employed to eliminate the ambiguities. Under normal circumstances there will only be two overloads provided, one of which must accept a pointer. If additional overloads are provided, they must be pointer to pointer variants.Which variations are actually provided depend upon how the function interacts with the object. If the parameter is declared const then the function can only call the object's const member functions and access its mutable member variables. In the case of the pointer variations, this means those that declare const MyObject* parameters. Those with const p parameters only mean the pointer will always point at the same object within the function body.Passing objects by reference is the preferred method of passing objects to functions whenever an object is guaranteed to be known to exist. Passing by pointer is often provided as an overload to cater for those cases where an object may not exist; in some cases this may in fact be the only function provided. Regardless, passing by reference or by pointer is effectively the same as passing the object itself. Thus if the parameter is declared non-const, any changes made to the function's parameter will be reflected in the original object.Passing objects by value is rarely used and should be avoided as much as is possible. When you pass an object by value, the object's copy constructor is automatically invoked. This is often unnecessary and can have an enormous impact upon your program's performance and memory consumption, especially with complex object's that contain embedded objects and/or inherit from other classes, each of which must be copied. However, there will be occasions where a function needs to modify an object where those changes would be undesirable. Thus passing by value would be deemed acceptable. The alternative would be to manually copy your object and then pass the copy by reference or by pointer instead.
Immutable values are easier to understand, track, and reason about, so prefer constants over variables wherever it is sensible and make const your default choice when you define a value: It's safe, it's checked at compile time, and it's integrated with C++'s type system." The same applies to const member functions, which essentially just make the "this" pointer const.
You pass by value when you do not want the original value to change and the value being passed is no more than 4 bytes long (or 8 bytes on a 64-bit system). If the value is greater than 4 bytes, you will incur a copy overhead, which can be expensive if the value is an array or other complex structure or object. If the value is passed as a const, the function will not alter its copy of the value. If the value is passed as non-const, the function can alter its copy of the value. Either way, the original value is unaffected. Pointers are always passed by value. That is, the pointer is copied by the function, leaving the original pointer unaffected. However, since the copied pointer points to the same memory as the original pointer, it's as if you had passed a reference to that memory, without copying that memory. If the pointer is passed as a const, the immutable members of the memory being pointed at will be unaffected by the function call. If it is non-const, it is expected that the memory being pointed at will be changed by the function call. If you do not wish the original memory to be altered, you must copy that memory and pass a pointer to the copy instead. Naturally, this will incur a performance penalty. Passing by reference passes the actual value being referenced, without copying. However, the compiler implements references as pointers so the same rules apply as for pointers. If the reference is non-const and you do not wish the value to be altered, copy the value and pass a reference to the copy instead. Passing a pointer by reference, rather than by value, is achieved by passing a pointer to the pointer, rather than a copy of the pointer. This allows the function to change the value of the original pointer. That is, the original pointer can be altered by the function to point at another memory location. When dealing with dynamic, multi-dimensional arrays, pointers to pointers (to pointers!) are fairly common, if only to avoid the costly copy overhead.