← All posts

Programação Orientada a Objetos em C++

Sep 4, 2026

Screenshot of a C++ console app calculating local gravity with the WGS84 ellipsoid model

A programação orientada a objetos (POO) é um paradigma (forma de estruturar linguagens de programação) de análise, projeto e programação de sistemas de software com foco em manutenção, escalabilidade e modularidade de código. Foi desenvolvida para ter alto nível de abstração e se aproximar mais de um "objeto real".

Sistemas são modelados por meio de características (atributos) e comportamentos (métodos) dos elementos nele envolvidos.

A POO é baseada em quatro pilares:

  • Abstração;
  • Encapsulamento;
  • Herança;
  • Polimorfismo.

Neste post cubro Abstração e Encapsulamento; Herança e Polimorfismo ficam para uma eventual continuação.

#Abstração

Uma abstração é uma maneira que nós encontramos de representar situações complexas mostrando apenas aquilo que realmente importa. Ela serve como base para a criação de sistemas complexos que sejam organizados, modulares e reutilizáveis.

No contexto da programação orientada a objetos (POO) significa criar modelos que focam naquilo que é essencial de um objeto / conceito.

C++
class MetodoPagamento {
public:
	virtual ~MetodoPagamento() = default;
	virtual void processar(double valor) const = 0;
};

class CartaoCredito : public MetodoPagamento {
private:
	std::string numeroCartao;
	
	bool validarCartao() const {
		return numeroCartao.length() == 16;
	}


public:
	explicit CartaoCredito(std::string numero) : numeroCartao(std::move(numero)) {}
	void processar(double valor) const override {
		if (validarCartao()) {
			std::cout << "processado r$: " << valor << " no cartao ..." << numeroCartao.substr(12) << ".\n";
		} else {
			std::cout << "falha: cartao invalido. \n";
		}
	}
};

class Pix : public MetodoPagamento {
private:
	std::string chavePix;
	
	std::string gerarCopiaCola() const {
		return "3123123...payload...";
	}

public:
	explicit Pix(std::string chave) : chavePix(std::move(chave)) {}
	
	void processar(double valor) const override {
		std::cout << "chave pix enviada " << chavePix << ". gerando transacao de r$ " << valor << ".\n";
	}
};

void finalizarCompra(const MetodoPagamento& metodo, double total) {
	metodo.processar(total);
}

int main() {
	auto cartao = std::make_unique<CartaoCredito>("1234567812345678");
	auto pix = std::make_unique<Pix>("teste@email.com");
	
	finalizarCompra(*cartao, 66.66);
	finalizarCompra(*pix, 69.90);
	
	return 0;
}

Aqui, métodos privados como a validação de cartão e gerar copia e cola cuidam da lógica interna de cada operação não expondo as etapas tomadas para a função finalizarCompra.

A função finalizarCompra depende apenas da abstração genérica, se um novo método, por exemplo, BoletoBancario for criado, a lógica de compra mantém-se inalterada.

#Encapsulamento

Objetos encapsulam tanto métodos quanto atributos, diferente da programação estruturada, em que funções agiam diretamente sobre os dados. Ou seja, comportamentos (métodos) e características (atributos) são agrupados em uma única entidade lógica, permitindo esconder determinadas características de um objeto.

O acesso restrito por meio de métodos controlados evita que os atributos sejam modificados de forma incorreta, preservando o estado e a consistência dos objetos.

Entenda o encapsulamento como um caixa eletrônico. Em um caixa eletrônico, é possível sacar, depositar e consultar saldo. Estas ações são realizadas por meio de operações seguras, você não pode acessar o cofre e pegar o dinheiro diretamente.

O encapsulamento mal feito é como um cofre aberto. Mudanças no estado dos objetos podem ser feitas de maneiras arbitrárias e não há controle ou segurança nas operações realizadas.

#Classes e Objetos

Um objeto é uma instanciação de uma classe. Uma classe é como um molde, receita ou projeto, estas possuem métodos (funções membro) e atributos (dados membro). Uma boa forma de entendermos classes é:

A classe é uma ideia, o objeto é a realidade.

Planta Baixa -> Casa Construída Classe -> Objeto

Os objetos possuem valores próprios para seus atributos, e após inicialização, um conjunto de atributos é denominado estado do objeto. Uma mesma classe pode gerar diversos objetos distintos.

#Atributos

Os atributos são dados abstraídos pelas classes e armazenados pelos objetos, o conjunto de atributos inicializado forma o estado do objeto. Em geral, atributos podem ser:

  • Públicos (acesso direto)
  • Privados (acesso restrito) É uma boa prática de encapsulamento que os atributos sejam privados sempre que possível. O acesso à estes atributos privados é feito por meio de métodos públicos.

#Métodos

Da mesma forma que os atributos, podem ser públicos ou privados. São funções definidas no escopo da classe e definem os comportamentos dos objetos. Elas podem acessar e modificar atributos privados da mesma classe, bem como invocar funções membro privadas.

Ilustrando estes conceitos com um exemplo "clássico" de orientação à objetos, o exemplo do carro:

C++
class Carro {
private:
	std::string marca;
	std::string modelo;
	int ano;
	int velocidade;
	
public:
	Carro(std::string m, std::string mod, int a)
    : marca(std::move(m)), modelo(std::move(mod)), ano(a), velocidade(0) {}
	
	void acelerar(int incremento) {
		velocidade += incremento;
		std::cout << marca << " acelerou para " << velocidade << " km/h.\n";
	}
	
	void frear(int decremento) {
		velocidade -= decremento;
		if (velocidade < 0) velocidade = 0;
		std::cout << marca << " reduziu para " << velocidade << "km/h.\n";
	}
	
	void exibirInfo() const {
		std::cout << marca << " " << modelo << " (" << ano << ") | Velocidade: " << velocidade << " km/h\n";
	}
};

int main() {
	Carro meuCarro("toyota", "corolla xei", 2004);
	
	meuCarro.exibirInfo();
	meuCarro.acelerar(60);
	meuCarro.frear(20);
	
	return 0;
}

Encapsulamento: os atributos estão protegidos contra modificações externas acidentais. Os métodos funcionam como a porta de entrada para interagir com o objeto e controlar seus dados internos com segurança.

#Declaração e Definição

Declaração (arquivo.h)

  • Lista de atributos e métodos da classe;
  • O que a classe promete oferecer;
  • Funciona como um contrato e define a interface da classe

Definição (arquivo .cpp)

  • Contém a implementação dos métodos declarados
  • Especifica o comportamento real da classe

#Calculadora.h

C++
#pragma once

class Calculadora {
private:
    int id;
    static int operacoesRealizadas;

public:
    explicit Calculadora(int id);

    double somar(double a, double b);
    void exibirInfo() const;

    static int getOperacoesRealizadas();
};

#Calculadora.cpp

C++
#include "Calculadora.h"
#include <iostream>

int Calculadora::operacoesRealizadas = 0;

Calculadora::Calculadora(int id) : id(id) {}

double Calculadora::somar(double a, double b) {
    operacoesRealizadas++;
    return a + b;
}

void Calculadora::exibirInfo() const {
    std::cout << "Calculadora [" << id << "] operacional.\n";
}

int Calculadora::getOperacoesRealizadas() {
    return operacoesRealizadas;
}

#Main.cpp

C++
#include "Calculadora.h"
#include <iostream>

int main() {
    Calculadora calcA(1);
    Calculadora calcB(2);

    calcA.exibirInfo();
    std::cout << "Soma via calcA: " << calcA.somar(10.5, 4.5) << "\n";
    std::cout << "Soma via calcB: " << calcB.somar(20.0, 30.0) << "\n";

    std::cout << "Total de operacoes globais: " 
              << Calculadora::getOperacoesRealizadas() << "\n";

    return 0;
}
  • '#pragma' once impede o compilador de processar declarações do arquivo .h mais de uma vez;

#Modificação de Atributos Privados

Conforme visto em Métodos, podemos modificar atributos privados somente por meio de funções membro da mesma classe. Aqui cabem os métodos de acesso, denominados Getters e Setters, estes servem como mediadores entre a leitura e a escrita em atributos privados, tendo em vista que é boa prática mantê-los privados.

Getter: método público que apenas retorna o valor de um atributo privado. Deve ser marcado como const para não modificar o estado do objeto. Setter: método público responsável por receber um valor e atribuí-lo a um campo privado, permitindo a validação do dado recebido antes de efetivar mudanças.

#Motivação Para o Uso de Atributos Privados

  1. Validação de Dados
  2. Atributos Read-Only
  3. Desacoplamento da Representação Interna

#Validação de Dados

Dados que são inseridos no sistema precisam ser validados para evitar erros, inconsistências e possíveis brechas de segurança. Exemplos simples podem ser a inserção de idade negativa, string de CPF inválida, número de telefone inválido.

#Atributos Read-Only

Um atributo que conta apenas com métodos Getter e nenhum Setter só pode ser definido na construção do objeto e nunca alterado.

#Desacoplamento da Representação Interna

A forma como o dado é armazenado na memória pode mudar sem que o código externo que o consome precise ser alterado, por exemplo, uma medição de temperatura pode ser armazenada em Kelvin internamente e retornar externamente um valor em Celsius calculado pelo método getter.

#Construtores

Classes devem ser instanciadas para criar objetos, podemos usar construtores para instanciar uma classe e inicializar seus atributos simultaneamente.

Os construtores são funções membros especiais da classe, tem o mesmo nome que a classe, não possuem nenhum retorno e são sempre chamados ao se criar um objeto.

Podemos classificar os construtores em três tipos:

  • Padrão Pessoa();
  • Parametrizado Pessoa(std::string _nome, float altura, unsigned int idade);
  • Cópia Pessoa(const Pessoa& p)

Caso nenhum construtor seja declarado / definido, o compilador gera um construtor do tipo padrão automaticamente.

#Construtor Padrão

O construtor padrão inicializa os atributos de um objeto com valores padrão.

C++
#include <iostream>
#include <string>

class Pessoa {
private:
    std::string nome;
    int idade;

public:
    Pessoa() : nome("Não informado"), idade(0) {}

    void exibir() const {
        std::cout << "Nome: " << nome << " | Idade: " << idade << " anos\n";
    }
};

int main() {

    Pessoa p1; 
    p1.exibir();

    return 0;
}

Também é possível em versões mais modernas (C++ 11 em diante) definir os valores padrão diretamente nos membros da classe e pedir ao compilador que gere o construtor padrão com = default

C++
class Pessoa {
private:
    std::string nome = "Não informado";
    int idade = 0;

public:
    Pessoa() = default;
};

Lembrando que não é possível modificar atributos constantes de uma classe, neste caso deve-se utilizar uma lista de inicialização no construtor, que nos permite iniciar atributos de uma classe antes do corpo do construtor.

O uso da lista de inicialização (antes das chaves do construtor) em certos casos é obrigatório para que o código compile:

  1. Atributos constantes;
  2. Atributos referenciados;
  3. Membros que não possuem construtor padrão;
  4. Repasse de argumentos ao construtor herdado de outra classe.

Os membros de uma classe são inicializados na ordem em que são declarados no corpo da classe, não na ordem em que aparecem na lista de inicialização do construtor. Se as duas ordens não baterem, o resultado pode ser um membro sendo inicializado a partir de outro que ainda não tem valor definido. A maioria dos compiladores avisa sobre isso com a flag -Wreorder (habilitada em -Wall):

Code
warning: field 'largura' will be initialized after field 'altura' [-Wreorder]
C++
class Carro {
	private:
		const std::string modelo;
		int ano;
		const int chassi;
		
	public:
		Carro(std::string m, int a, int c)
			: modelo(std::move(m)), ano(a), chassi(c) {}
		
		void exibir() const {
			std::cout << "modelo: " << modelo << "|| ano: " << ano << "| chassi: " << chassi << "\n";
		}
};

int main() {
	Carro meuCarro("Civic", 2022, 12121);
	meuCarro.exibir();
	
	return 0;
}

Como chassié constante, tentar atribuir algo como chassi = c;dentro das chaves {}causaria erro de compilação. A única saída é a aplicação de uma lista de inicialização, a string já é criada com o texto final nesse caso.

#Construtor Parametrizado

O construtor parametrizado, como o nome sugere, aceita um ou mais argumentos para inicializar um objeto com valores específicos no momento de criação ao invés de atribuí-los posteriormente.

C++
class Retangulo {
public:
    Retangulo(double base, double altura) : base(base), altura(altura) {}

    double area() const { return base * altura; }

private:
    double base;
    double altura;
};

int main() {
    Retangulo r(4.0, 5.5);
    return 0;
}

Uma classe pode ter tanto o construtor padrão quanto o construtor parametrizado.

#Construtor de Cópia

Um construtor de cópia permite a criação de uma cópia de um objeto previamente instanciado. É automaticamente invocado quando um objeto é inicializado à partir de outro, por exemplo:

C++
Retangulo r2 = r1;
Retangulo r2(r1);

Também é invocado quando um objeto é passado por valor para uma função, ou quando um objeto é retornado por valor.

A passagem por referência é uma exigência estrita para compilação. O primeiro parâmetro de um construtor de cópia deve ser um dos seguintes:

C++
P(P&)
P(const P&)
P(volatile P&)
P(const volatile P&)

Passar um argumento por valor significa criar uma cópia dele mesmo, e como a função chamada é um construtor de cópia, construir o parâmetro passado exige chamar a função que está sendo chamada, resultando em uma recursão infinita. O C++ não permite a compilação, retornando um erro claro, normalmente:

error: invalid constructor; you probably meant 'P(const P&)'

Passagem de argumentos por referência remedia este problema, pois não cria um novo objeto, apenas referencia um objeto já existente.

#Destrutores

Um destrutor é uma função membro que é automaticamente executada quando o ciclo de vida de um objeto termina. Ele libera os recursos adquiridos pelo objeto criado durante sua existência, como memória, arquivos, conexões, etc. Uma classe possui somente um destrutor, este não recebe argumentos e não tem retorno. Sintaticamente escrito com o nome da classe prefixado por um til (~):

C++
class FileHandler {
public:
    explicit FileHandler(const std::string& path) {
        file.open(path);
    }

    ~FileHandler() {
        if (file.is_open()) file.close();
    }

private:
    std::fstream file;
};

Destrutores sendo disparados automaticamente no end-of-scope é o principal mecanismo do C++ para evitar erros no gerenciamento de recursos. O C++ amarra o tempo de vida de um recurso ao de um objeto e o destrutor garante a liberação desse recurso. Essa característica se chama RAII (Resource Acquisition Is Initialization).

#Exemplo

C++
#include <iostream>
#include <string>

class ContaBancaria {
private:
	std::string titular;
	const int numeroConta;
	double saldo;

	bool validarValor(double valor) const {
		return valor > 0;
	}

public:
	ContaBancaria()
		: titular("Não informado"), numeroConta(0), saldo(0.0) {}

	ContaBancaria(std::string _titular, int _numeroConta, double _saldoInicial = 0.0)
		: titular(std::move(_titular)), numeroConta(_numeroConta), saldo(_saldoInicial) {}

	ContaBancaria(const ContaBancaria& outra)
		: titular(outra.titular), numeroConta(outra.numeroConta), saldo(outra.saldo) {
		std::cout << "Copiando conta de " << titular << ".\n";
	}

	~ContaBancaria() {
		std::cout << "Conta de " << titular << " encerrada.\n";
	}

	std::string getTitular() const { return titular; }
	int getNumeroConta() const { return numeroConta; }
	double getSaldo() const { return saldo; }

	void depositar(double valor) {
		if (!validarValor(valor)) {
			std::cout << "Valor de depósito inválido.\n";
			return;
		}
		saldo += valor;
		std::cout << titular << " depositou r$" << valor << ". Saldo: r$" << saldo << ".\n";
	}

	void sacar(double valor) {
		if (!validarValor(valor) || valor > saldo) {
			std::cout << "Saque inválido.\n";
			return;
		}
		saldo -= valor;
		std::cout << titular << " sacou r$" << valor << ". Saldo: r$" << saldo << ".\n";
	}
};

int main() {
	ContaBancaria c1("Maria", 1001, 500.0);
	c1.depositar(200.0);
	c1.sacar(150.0);

	ContaBancaria c2 = c1;
	c2.sacar(100.0);

	std::cout << "Saldo de " << c1.getTitular() << ": r$" << c1.getSaldo() << ".\n";
	std::cout << "Saldo de " << c2.getTitular() << ": r$" << c2.getSaldo() << ".\n";

	return 0;
}

#Referências

Constructors and member initializer lists - cppreference.com

Copy constructors - cppreference.com

Destructors - cppreference.com

Construtores (C++) - Microsoft Learn

Introduction to constructors - LearnCpp

© 2026 Paulo Amarante. No rights reserved.