Funciones miembro const
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 29 de 104.
Has visto la palabra clave const en los métodos getter de la lección anterior. Pero ¿qué significa exactamente cuando const aparece después de la lista de parámetros de una función? Una función miembro const promete no modificar ninguna variable miembro del objeto.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // función miembro const
return width * height; // Se permite leer los miembros
}
void setWidth(int w) { // Non-const - modifica el objeto
width = w;
}
};El const después de la lista de parámetros le indica al compilador: «Esta función no cambiará el estado del objeto». Si intentas modificar una variable miembro dentro de una función const, el compilador producirá un error.
Esto se vuelve esencial al trabajar con objetos const o referencias const. Un objeto const solo puede llamar a funciones miembro const:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() es const
// rect.setWidth(10); // ERROR - setWidth() no es const
}Marcar las funciones como const cuando no modifican el objeto es una buena práctica. Documenta tu intención, permite que la función trabaje con objetos const y ayuda al compilador a detectar modificaciones accidentales. Cualquier función miembro que solo lea datos debe marcarse como const.
Desafío
FácilConstruyamos un convertidor de temperatura que demuestre cuándo y por qué usar funciones miembro const. Crearás una clase en la que algunos métodos solo leen datos (y deberían ser const), mientras que otros modifican el estado del objeto.
Crearás dos archivos para organizar tu código:
Temperature.h: Define una claseTemperatureque almacena un valor de temperatura y proporciona varias formas de leerlo y modificarlo. Tu clase debería tener:- Un miembro privado
celsius(double) para almacenar la temperatura - Un constructor que acepte un valor inicial en Celsius
- Un método
getCelsius()que devuelva el valor almacenado. Debe ser const, ya que solo lee datos - Un método
getFahrenheit()que calcule y devuelva la temperatura en Fahrenheit usando la fórmulacelsius * 9.0 / 5.0 + 32.0, también const, ya que no modifica nada - Un método
getKelvin()que devuelva la temperatura en Kelvin usandocelsius + 273.15: también const - Un método
setCelsius(double value)que actualice la temperatura almacenada. No puede ser const, ya que modifica el objeto - Un método
adjustBy(double delta)que sume el delta a la temperatura actual, también no const
- Un miembro privado
main.cpp: Demuestra cómo funcionan las funciones miembro const tanto con objetos normales como con objetos const. Lee un valor de temperatura inicial de la entrada y, después:- Crea un objeto
Temperaturecon el valor introducido - Imprime
"Initial: <celsius>C = <fahrenheit>F = <kelvin>K" - Ajusta la temperatura en
10.0grados - Imprime
"After adjustment: <celsius>C" - Crea una función auxiliar
void printReadings(const Temperature& temp)que acepte una referencia const e imprima"Reading: <celsius>C, <fahrenheit>F". Esta función solo puede llamar a métodos const sobre temp - Llama a
printReadings()con tu objeto de temperatura - Establece la temperatura en
0.0(punto de congelación) - Imprime
"Freezing point: <celsius>C = <fahrenheit>F"
- Crea un objeto
Da formato a todos los valores de temperatura con un decimal utilizando std::fixed y std::setprecision(1) de <iomanip>.
La idea clave aquí es que printReadings() recibe una referencia const, por lo que solo puede llamar a métodos marcados como const. Por eso es importante marcar correctamente tus métodos getter como const. Esto permite que funcionen en contextos en los que el objeto no se puede modificar.
Pruébalo tú mismo
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: Implementa los métodos de la clase Temperature aquí
// Constructor
Temperature::Temperature(double initialCelsius) {
// TODO: Inicializa celsius
}
// TODO: Implementa getCelsius() como const
// TODO: Implementa getFahrenheit() como const
// TODO: Implementa getKelvin() como const
// TODO: Implementa setCelsius(double value)
// TODO: Implementa adjustBy(double delta)
// TODO: Crea una función auxiliar printReadings que tome un const Temperature&
// e imprime "Reading: <celsius>C, <fahrenheit>F"
// Note: ¡Esta función solo puede llamar métodos const en temp!
int main() {
double initialTemp;
cin >> initialTemp;
// Establece el formato de salida
cout << fixed << setprecision(1);
// TODO: Crea un objeto Temperature con el valor de entrada
// TODO: Imprime "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"
// TODO: Ajusta la temperatura en 10.0 grados
// TODO: Print "After adjustment: <celsius>C"
// TODO: Llama a printReadings() con tu objeto Temperature
// TODO: Set the temperature to 0.0 (freezing point)
// TODO: Print "Freezing point: <celsius>C = <fahrenheit>F"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genéricoPractica por tu cuenta: Compilador de C++ online