Un ejemplo de donde un puntero constante es altamente aplicable puede demostrarse de esta manera. Considere que tiene una clase con una matriz dinámica dentro y desea pasar el acceso del usuario a la matriz pero sin otorgarles los derechos para cambiar el puntero. Considerar:
#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());
}
Que produce:
Datos de entrada
poner datos
Pero si intentamos esto:
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
}
Obtenemos:
error: lvalue requerido como operando izquierdo de la asignación // ¡Drat falló nuevamente!
Claramente, podemos modificar el contenido de la matriz, pero no el puntero de la matriz. Es bueno si desea asegurarse de que el puntero tenga un estado coherente cuando se lo devuelva al usuario. Sin embargo, hay una trampa:
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!
}
Todavía podemos eliminar la referencia de memoria del puntero, incluso si no podemos modificar el puntero en sí.
Entonces, si desea que la referencia de memoria siempre apunte a algo (IE nunca se modifica, similar a cómo funciona actualmente una referencia), entonces es altamente aplicable. Si desea que el usuario tenga acceso completo y lo modifique, entonces no-const es para usted.
Editar:
Después de observar el comentario okorz001 de no poder asignar debido a que GetArray () es un operando de valor correcto, su comentario es completamente correcto, pero lo anterior aún se aplica si tuviera que devolver una referencia al puntero (supongo que supuse que GetArray era refiriendo una referencia), por ejemplo:
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
}
Volverá en el primer resultado en un error:
error: asignación de ubicación de solo lectura 'Temp.TestA :: GetArray ()'
Pero el segundo ocurrirá alegremente a pesar de las posibles consecuencias en la parte inferior.
Obviamente, se planteará la pregunta "¿por qué querría devolver una referencia a un puntero"? Hay casos excepcionales en los que necesita asignar memoria (o datos) directamente al puntero original en cuestión (por ejemplo, construir su propio front-end malloc / free o new / free), pero en esos casos es una referencia no constante . Una referencia a un puntero constante No me he encontrado con una situación que lo justifique (¿a menos tal vez como variables de referencia constantes declaradas en lugar de tipos de retorno?).
Considere si tenemos una función que toma un puntero constante (versus uno que no lo hace):
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");
}
El error en el const produce así el mensaje:
error: disminución del parámetro de solo lectura 'Datos'
Lo cual es bueno ya que probablemente no queremos hacer eso, a menos que queramos causar los problemas indicados en los comentarios. Si editamos el decremento en la función const, ocurre lo siguiente:
No
Const: A Const: B
Claramente, aunque A es 'Datos [1]', se trata como 'Datos [0]' porque el puntero NonConst permitió la operación de disminución. Con el const implementado, como escribe otra persona, detectamos el error potencial antes de que ocurra.
Otra consideración principal es que un puntero constante se puede usar como una pseudo referencia, en el sentido de que no se puede cambiar el punto de referencia (uno se pregunta, si tal vez así fue como se implementó). Considerar:
int main()
{
int A = 10;
int * const B = &A;
*B = 20; //This is permitted
printf("%d\n",A);
B = NULL; //This produces an error
}
Al intentar compilar, produce el siguiente error:
error: asignación de la variable de solo lectura 'B'
Lo cual es probablemente algo malo si se quisiera una referencia constante a A. Si B = NULL
se comenta, el compilador felizmente nos permitirá modificar*B
y, por lo tanto, A. Esto puede no parecer útil con ints, pero considere si tenía una sola posición de una aplicación gráfica donde deseaba un puntero no modificable que se refiriera a él y que pudiera pasar alrededor.
Su uso es variable (disculpe el juego de palabras involuntario), pero se usa correctamente, es otra herramienta en la caja para ayudar con la programación.