Funções Membro Const
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 29 de 104.
Você viu a palavra-chave const nos métodos getter da lição anterior. Mas o que exatamente significa quando const aparece após a lista de parâmetros de uma função? Uma função membro const promete não modificar nenhuma variável membro do objeto.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // função membro const
return width * height; // A leitura de membros é permitida
}
void setWidth(int w) { // Non-const - modifica o objeto
width = w;
}
};O const após a lista de parâmetros informa ao compilador: "Esta função não alterará o estado do objeto." Se você tentar modificar uma variável-membro dentro de uma função const, o compilador produzirá um erro.
Isso se torna essencial ao trabalhar com objetos const ou referências const. Um objeto const só pode chamar funções-membro const:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() é const
// rect.setWidth(10); // ERROR - setWidth() não é const
}Marcar funções como const quando elas não modificam o objeto é uma boa prática. Isso documenta sua intenção, permite que a função trabalhe com objetos const e ajuda o compilador a detectar modificações acidentais. Qualquer função-membro que apenas leia dados deve ser marcada como const.
Desafio
FácilVamos criar um conversor de temperatura que demonstra quando e por que usar funções membro const. Você criará uma classe na qual alguns métodos apenas leem dados (e devem ser const), enquanto outros modificam o estado do objeto.
Você criará dois arquivos para organizar seu código:
Temperature.h: Defina uma classeTemperatureque armazena um valor de temperatura e oferece várias maneiras de lê-lo e modificá-lo. Sua classe deve ter:- Um membro privado
celsius(double) para armazenar a temperatura - Um construtor que recebe um valor inicial em Celsius
- Um método
getCelsius()que retorna o valor armazenado. Ele deve ser const, pois apenas lê dados - Um método
getFahrenheit()que calcula e retorna a temperatura em Fahrenheit usando a fórmulacelsius * 9.0 / 5.0 + 32.0, também const, pois não modifica nada - Um método
getKelvin()que retorna a temperatura em Kelvin usandocelsius + 273.15: também const - Um método
setCelsius(double value)que atualiza a temperatura armazenada. Ele não pode ser const, pois modifica o objeto - Um método
adjustBy(double delta)que adiciona o delta à temperatura atual, também não const
- Um membro privado
main.cpp: Demonstre como as funções membro const funcionam com objetos comuns e const. Leia um valor inicial de temperatura da entrada e, em seguida:- Crie um objeto
Temperaturecom o valor de entrada - Imprima
"Initial: <celsius>C = <fahrenheit>F = <kelvin>K" - Ajuste a temperatura em
10.0graus - Imprima
"After adjustment: <celsius>C" - Crie uma função auxiliar
void printReadings(const Temperature& temp)que recebe uma referência const e imprime"Reading: <celsius>C, <fahrenheit>F". Essa função só pode chamar métodos const em temp - Chame
printReadings()com seu objeto de temperatura - Defina a temperatura como
0.0(ponto de congelamento) - Imprima
"Freezing point: <celsius>C = <fahrenheit>F"
- Crie um objeto
Formate todos os valores de temperatura com uma casa decimal usando std::fixed e std::setprecision(1) de <iomanip>.
A principal ideia aqui é que printReadings() recebe uma referência const, portanto só pode chamar métodos marcados como const. É por isso que marcar corretamente seus métodos getter como const é importante. Isso permite que eles funcionem em contextos nos quais o objeto não pode ser modificado.
Experimente você mesmo
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: Implemente os métodos da classe Temperature aqui
// Construtor
Temperature::Temperature(double initialCelsius) {
// TODO: Inicialize celsius
}
// TODO: Implemente getCelsius() como const
// TODO: Implemente getFahrenheit() como const
// TODO: Implemente getKelvin() como const
// TODO: Implemente setCelsius(double value)
// TODO: Implemente adjustBy(double delta)
// TODO: Crie uma função auxiliar printReadings que recebe um const Temperature&
// e imprime "Reading: <celsius>C, <fahrenheit>F"
// Note: Esta função só pode chamar métodos const em temp!
int main() {
double initialTemp;
cin >> initialTemp;
// Define a formatação de saída
cout << fixed << setprecision(1);
// TODO: Crie um objeto Temperature com o valor de entrada
// TODO: Imprima "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"
// TODO: Ajuste a temperatura em 10.0 graus
// TODO: Print "After adjustment: <celsius>C"
// TODO: Chame printReadings() com o seu objeto de temperatura
// TODO: Set the temperature to 0.0 (freezing point)
// TODO: Print "Freezing point: <celsius>C = <fahrenheit>F"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online