Última actualización:2026-10-12
const en C++: constantes y modificación¶
Sobre el autor¶
Cuenta pública de WeChat:

1. Significado de const¶
Un tipo constante es un tipo declarado con el modificador de tipo const; el valor de las variables u objetos de tipo constante no puede actualizarse.
2. Función de const¶
- Permite definir constantes
+ Diferencia entre las constantes const y las constantes definidas con la macro `#define`:
> <strong><s>Las constantes const tienen un tipo, de modo que el compilador puede realizar comprobaciones de seguridad; las constantes definidas con la macro #define no tienen tipo de datos, son un simple reemplazo de cadenas y no permiten comprobaciones de seguridad.</s></strong>Gracias a dos expertos por señalar el problema aquí, ver: [issue](https://github.com/Light-City/CPlusPlusThings/issues/5)
+ Las constantes const admiten todos los tipos.
+ En los demás casos, solo se trata de una variable cualificada con `const`; no hay que confundirla con una constante.
- Evita modificaciones, ejerce una función de protección y aumenta la robustez del programa
+ Desde el punto de vista del ensamblador, una constante definida con const solo proporciona la dirección de memoria correspondiente, mientras que `#define` proporciona un inmediato.
+ Una constante definida con const tiene una sola copia durante la ejecución del programa, mientras que las constantes definidas con `#define` tienen varias copias en memoria.
3. Los objetos const son por defecto locales al archivo¶
Nota: las variables no const son extern por defecto. Para que una variable const pueda utilizarse desde otros archivos, debe especificarse explícitamente como extern en el archivo.
Acceso a variables no modificadas con const desde archivos distintos
// file1.cpp
int ext;
// file2.cpp
#include<iostream>
extern int ext;
int main(){
std::cout<<(ext+10)<<std::endl;
}
Acceso a constantes const desde archivos distintos
// extern_file1.cpp
extern const int ext=12;
// extern_file2.cpp
#include<iostream>
extern const int ext;
int main(){
std::cout<<ext<<std::endl;
}
Resumen:
Se puede observar que las variables no modificadas con const no necesitan una declaración explícita de extern, mientras que las constantes const deben declararse explícitamente como extern y deben inicializarse. Como una constante no puede modificarse después de definirse, debe inicializarse en el momento de la definición.
4. Definir constantes¶
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
5. Punteros y const¶
Hay cuatro formas de combinar const con punteros:
const char * a; // 指向const对象的指针或者说指向常量的指针。
char const * a; // 同上
char * const a; // 指向类型对象的const指针。或者说常指针、const指针。
const char * const a; // 指向const对象的const指针。
Resumen:
Si const está a la izquierda de*, entonces const modifica la variable apuntada por el puntero, es decir, el punto señalado es constante;
si const está a la derecha de*, const modifica el puntero mismo, es decir, el puntero en sí es constante.
Otra forma de interpretarlo
Leyendo en inglés de derecha a izquierda y usando to como frontera: lo que va antes de to describe las características del puntero y lo que va después de to describe las del objeto apuntado
const char * p; //p is a pointer to const char
char const * p; //同上
char * const p; //p is a const pointer to char
const char * const p; //p is a const pointer to const char
```Cuando el puntero recibe la característica const, el puntero no puede cambiar la dirección a la que apunta<br>
Cuando el objeto apuntado tiene característica char, el contenido puede modificarse a través del puntero, por ejemplo: *char='y';<br>
Cuando el objeto apuntado tiene característica const char, el contenido no puede modificarse a través del puntero<br>
Su uso concreto es el siguiente:
(1) **Puntero a constantes**
```cpp
const int *ptr;
*ptr = 10; // error
Además, tampoco se puede usar un puntero void* para guardar la dirección de un objeto const; debe usarse un puntero de tipo const void* para guardar la dirección de un objeto const.
Asignar la dirección de un objeto no const a un puntero a objeto const:
No podemos modificar mediante el puntero ptr el valor de val, ¡aunque apunte a un objeto no const!No podemos usar un puntero a objeto const para modificar el objeto subyacente; sin embargo, si ese puntero apunta a un objeto no const, el objeto apuntado puede modificarse por otros medios. El valor apuntado por un puntero const puede modificarse, ¡solo que no a través del puntero a objeto const! Modificación como sigue:
Resumen:
1. Con un puntero a constantes no se puede modificar mediante el puntero el valor del objeto.
2. No se puede usar un puntero void*para guardar la dirección de un objeto const; debe usarse un puntero de tipo const void*para guardar la dirección de un objeto const.
3. Está permitido asignar la dirección de un objeto no const a un puntero a objeto const; si se desea modificar el valor del objeto apuntado, debe hacerse por otros medios, no directamente a través del puntero actual.
(2) Puntero constante
Un puntero const debe inicializarse; el valor apuntado por un puntero const puede modificarse, pero no puede cambiar el objeto al que apunta.
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
ptr = &num1; // error! const指针不能修改指向!
cout<<*ptr<<endl;
}
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
*ptr = 1;
cout<<*ptr<<endl;
}
Por último, cuando se asigna a ptr la dirección de una constante const, como ptr apunta a una variable y no a una constante const, se produce un error: ¡error de const int* -> int *!
#include<iostream>
using namespace std;
int main(){
const int num=0;
int * const ptr=# // error! const int* -> int*
cout<<*ptr<<endl;
}
*ptr o a const int *const ptr, ¡ambas versiones funcionan con normalidad!
(3) Puntero constante a constantes
Entendidos los dos casos anteriores, este caso resulta más fácil de entender:
ptr es un puntero const que apunta a un objeto const de tipo int.6. Uso de const en funciones¶
const modifica el valor de retorno de la función
Esto tiene básicamente el mismo significado que cuando const modifica variables ordinarias y punteros:
(1) const int
Esto en sí carece de sentido, ¡porque el valor devuelto ya se asigna a otras variables!(2) const int*
El contenido apuntado por el puntero no cambia.(3) int *const
El puntero mismo no puede cambiarse.const modifica los parámetros de la función
(1) El parámetro recibido y el puntero mismo no pueden modificarse dentro de la función, ¡carece de sentido!
Indica que el parámetro no puede modificarse dentro del cuerpo de la función, pero aquí no tiene ningún sentido: var es en sí un parámetro formal y no cambia dentro de la función. Lo mismo ocurre cuando el parámetro formal recibido es un puntero.Cuando el parámetro de entrada se pasa "por valor", como la función genera automáticamente una variable temporal para copiarlo, dicho parámetro de entrada no necesita protección, así que no hace falta añadir const.
(2) El contenido apuntado por el parámetro puntero es constante e inmutable
Aquí src es un parámetro de entrada y dst es un parámetro de salida. Al añadir const a src, si alguna instrucción del cuerpo de la función intenta modificar el contenido de src, el compilador señalará el error. Este es uno de los efectos de añadir const.(3) El parámetro es una referencia, para ganar eficiencia y a la vez evitar modificaciones.
Para los parámetros de tipos de datos no internos, una función declarada como void func(A a) está condenada a ser poco eficiente, porque en el cuerpo de la función se generará un objeto temporal de tipo A para copiar el parámetro a, y la construcción, copia y destrucción del objeto temporal consumen tiempo.Para mejorar la eficiencia, la función puede declararse como void func(A &a), porque el "paso por referencia" solo toma prestado el alias del parámetro y no necesita generar un objeto temporal.
Pero la función void func(A &a) tiene un inconveniente:
el "paso por referencia" podría modificar el parámetro a, y eso no es lo que deseamos. La solución es muy sencilla: añadir el modificador const, de modo que la función finalmente queda como void func(const A &a).
Por analogía, ¿debería reescribirse void func(int x) como void func(const int &x) para mejorar la eficiencia? No hay ninguna necesidad, porque los parámetros de tipos de datos internos no pasan por procesos de construcción y destrucción, y copiarlos es muy rápido: la eficiencia del "paso por valor" y del "paso por referencia" es prácticamente la misma.
Resumen:
1. Para los parámetros de entrada de tipos de datos no internos, conviene cambiar el "paso por valor" por un "paso por const referencia", con el objetivo de mejorar la eficiencia. Por ejemplo, cambiar void func(A a) por void func(const A &a).
2. Para los parámetros de entrada de tipos de datos internos, no hay que cambiar el "paso por valor" por un "paso por const referencia". De lo contrario, no solo no se gana eficiencia, sino que se reduce la claridad de la función. Por ejemplo, void func(int x) no debería cambiarse por void func(const int &x).
Con lo anterior se resuelven dos preguntas de entrevista:
- Si una función necesita recibir un puntero, ¿hay que añadir const a ese puntero y qué diferencia hay según la posición en la que se coloque const;
- Si la función que escribes necesita recibir como parámetro una instancia de un tipo complejo, qué diferencia hay entre pasar el parámetro por valor o por referencia, y cuándo hay que añadir const al parámetro de referencia recibido.
7. Uso de const en clases¶
En una clase, toda función que no modifique los miembros de datos debería declararse de tipo const. Si al escribir una función miembro const modificamos por descuido un miembro de datos, o llamamos a otra función miembro no const, el compilador señalará el error, lo que sin duda mejora la robustez del programa.
Las funciones miembro declaradas con la palabra clave const se denominan funciones miembro constantes. Solo las funciones miembro constantes pueden operar con constantes o con objetos constantes; las funciones miembro declaradas sin la palabra clave const no pueden utilizarse para operar con objetos constantes.
Las variables miembro const de una clase deben inicializarse mediante la lista de inicialización, como se muestra a continuación:
class Apple{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
Por ejemplo:
// apple.cpp
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
// apple.cpp
Apple::Apple(int i) : apple_number(i)
{
}
int Apple::add(int num)
{
take(num);
return 0;
}
int Apple::add(int num) const
{
take(num);
return 0;
}
void Apple::take(int num) const
{
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const
{
take(1);
// add(); // error
return apple_number;
}
int main()
{
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
// main.cpp
```> Compilación: bazel run basic_content/const/class_const/first_example:main<br>
En este momento se produce un error: en el método getCount() anterior se llama a un método add, y como el método add no está modificado con const, la ejecución da error. Es decir, una función miembro const solo puede acceder a funciones miembro const.
Cuando la llamada se cambia a:
Además de inicializar las constantes const con la lista de inicialización como se ha visto, también podemos hacerlo de las siguientes maneras:
Primera: combinar la definición de la constante con static, es decir:
Segunda: inicializarla fuera: Por supuesto, si compilas con C++11, puedes inicializar directamente en la definición y escribir directamente: ¡Ambas formas están soportadas en C++11!¡Basta con añadir -std=c++11 al compilar!
Aquí se ha mencionado static; a continuación, brevemente:
En C++, las variables miembro estáticas static no pueden inicializarse dentro de la clase. Dentro de la clase solo hay una declaración; la definición debe estar fuera del cuerpo de la definición de la clase, normalmente en el archivo de implementación de la clase.
Declaración en la clase:
Uso en el archivo de implementación de la clase: En este punto, C++11 no permite declarar e inicializar a la vez, es decir, el uso descrito anteriormente.Temas relacionados¶
- Funciones virtuales puras y clases abstractas en C++ | C++那些事
- Aserciones assert en C++ | C++那些事
- Campos de bits en C++ | C++那些事
- Implementar el polimorfismo de C++ en lenguaje C | C++那些事
- La familia completa de const en C++ | C++那些事
- El operador de desreferencia * en C++ | C++那些事
- Inicialización y asignación en C++ | C++那些事
- Formas de devolver varios valores en C++ | C++那些事
- La palabra clave mutable en C++ | C++那些事
- C++ restrict y optimización del compilador | C++那些事