TIC 5 · C++ · Programação Orientada a Objetos

Aprender
Linguagem C++

Do C ao C++ moderno — OOP, STL, templates, smart pointers e tudo o que precisas para escrever código eficiente e elegante.

▶ Começar aqui 🏋️ Exercícios 📋 Referência Rápida 🌐 Compiladores Online
hello.cpp — o primeiro programa em C++
// hello.cpp — C++ moderno com classes
#include <iostream>
#include <string>

class Saudacao {
    std::string nome;
public:
    Saudacao(std::string n) : nome(n) {}
    void dizer() const {
        std::cout << "Olá, " << nome << "!\n";
    }
};

int main() {
    Saudacao s("Mundo");
    s.dizer();
    return 0;
}
$ g++ -std=c++17 hello.cpp -o hello && ./hello
Olá, Mundo!
1985Ano de criação
#2TIOBE Index
10Módulos de estudo
35+Exercícios + soluções
01 · Fundamentos

📖 O que é a linguagem C++?

Antes de escrever código, é fundamental compreender porque C++ continua a ser uma das linguagens mais poderosas e versáteis da computação moderna.

O C++ é uma extensão da linguagem C criada por Bjarne Stroustrup nos laboratórios Bell a partir de 1979, tornando-se oficial em 1985. Nasceu como "C com Classes" e evoluiu para integrar múltiplos paradigmas: programação procedural, orientada a objetos, genérica e funcional.

Hoje, o C++ está no núcleo dos browsers (Chrome, Firefox), motores de jogos (Unreal Engine), bases de dados (MySQL, MongoDB), sistemas embebidos, motores de IA (TensorFlow, PyTorch) e toda a infraestrutura de alto desempenho. C++ oferece o controlo do C com a expressividade de uma linguagem moderna.

C vs C++ — principais diferenças C++ é um superconjunto de C
C (procedural)
/* Sem classes — struct + funções soltas */
typedef struct {
    char nome[50];
    int  idade;
} Pessoa;

void cumprimentar(Pessoa* p) {
    printf("Olá, %s!\n", p->nome);
}
C++ (orientado a objetos)
// Classes encapsulam dados e comportamento
class Pessoa {
    std::string nome;
    int idade;
public:
    Pessoa(std::string n, int i);
    void cumprimentar() const;
};
🟢
C++ é a linguagem certa quando...
  • Precisas de desempenho máximo com abstração
  • Desenvolves jogos, motores gráficos ou simulações
  • Trabalhas com sistemas embebidos avançados
  • Implementas estruturas de dados e algoritmos
  • Crias bibliotecas de alto desempenho
  • Programas para robótica, IA ou computação científica
🔴
C++ não é ideal quando...
  • Desenvolves aplicações web ou APIs simples
  • A prioridade é velocidade de desenvolvimento
  • A equipa não tem experiência com gestão manual de memória
  • Precisas de prototipagem rápida
  • O projeto envolve scripting ou automação simples
🏗️
Orientação a Objetos
Classes, herança, polimorfismo e encapsulamento. Organiza código complexo em estruturas reutilizáveis e mantíveis.
📦
Templates & Genéricos
Escreve código uma vez, funciona para qualquer tipo. A STL — vector, map, sort — está toda baseada em templates.
🔒
Smart Pointers
unique_ptr, shared_ptr e weak_ptr eliminam memory leaks sem sacrificar controlo. RAII garante segurança automática.
Zero-cost Abstractions
Abstração sem custo em runtime. Classes bem desenhadas compilam para o mesmo assembly que C manual equivalente.
🗃️
STL — Standard Library
Coleções de dados (vector, map, set), algoritmos (sort, find, transform) e utilitários prontos a usar.
🔄
Compatibilidade com C
C++ é um superconjunto de C. Todo o código C válido funciona em C++. Reutiliza bibliotecas C existentes sem esforço.
💡 Analogia: Aprender C++ é como passar de conduzir um carro manual para pilotar um avião. Muito mais capacidades, mais sistemas para gerir — mas com a mesma lógica de base. Quem vem do C já sabe conduzir; agora aprende a voar.
02 · Estrutura

📚 Programa de Formação

Segue esta ordem para uma progressão lógica — cada módulo constrói sobre o anterior. Clica num módulo para ver os tópicos e um exemplo de código.

01
De C para C++ — Primeiros Passos
Diferenças de sintaxe, cout/cin, namespaces, compilar com g++.
Iniciante
Diferenças C vs C++: <iostream> vs <stdio.h>
std::cout, std::cin, std::endl e '\n'
namespace std e using namespace std
Tipo bool nativo (true/false)
Referências (&) vs ponteiros
Compilar com g++ -std=c++17
Default arguments em funções
Function overloading
c_para_cpp.cpp
#include <iostream>
#include <string>
using namespace std;

// Função sobrecarregada (overloading)
void imprimir(int n)    { cout << "int: "    << n << endl; }
void imprimir(string s) { cout << "string: " << s << endl; }
void imprimir(double d) { cout << "double: " << d << endl; }

int main() {
    string nome;
    cout << "O teu nome: ";
    cin >> nome;
    imprimir(nome);   // chama versão string
    imprimir(42);    // chama versão int
    imprimir(3.14);  // chama versão double
    return 0;
}
02
Classes e Objetos
Encapsulamento, construtores, destrutores, membros e métodos.
Iniciante
Definição de class e struct em C++
public, private, protected
Construtores e lista de inicialização
Destrutor ~Classe()
Métodos const e não-const
this pointer
Separação .h e .cpp
Getter e Setter — encapsulamento
classes.cpp
class Retangulo {
    double largura, altura;  // private por defeito
public:
    // Construtor com lista de inicialização
    Retangulo(double l, double a) : largura(l), altura(a) {}

    double area()      const { return largura * altura; }
    double perimetro() const { return 2 * (largura + altura); }

    // Setters com validação
    void setLargura(double l) { if (l > 0) largura = l; }
};
03
Herança e Polimorfismo
Hierarquias de classes, métodos virtuais, classes abstratas.
Intermédio
Herança simples: class B : public A
Herança múltipla e problemas
Métodos virtual e override
Polimorfismo dinâmico (vtable)
Classes abstratas e métodos puros = 0
Destrutor virtual — porquê é essencial
dynamic_cast e typeid
Padrão de design Strategy com polimorfismo
polimorfismo.cpp
class Forma {  // classe abstrata
public:
    virtual double area() const = 0;  // método puro
    virtual ~Forma() = default;
    void info() const {
        cout << "Área: " << area() << "\n";
    }
};

class Circulo : public Forma {
    double raio;
public:
    Circulo(double r) : raio(r) {}
    double area() const override {
        return 3.14159 * raio * raio;
    }
};
04
Sobrecarga de Operadores
Redefinir +, -, ==, << e outros para os teus próprios tipos.
Intermédio
Operadores aritméticos: +, -, *, /
Operadores de comparação: ==, !=, <, >
Operador de inserção << para cout
Operador de atribuição =
Operador de índice []
Operadores como funções membro vs livres
Operador de conversão implícita
sobrecarga.cpp
class Vetor2D {
public:
    double x, y;
    Vetor2D(double x, double y) : x(x), y(y) {}

    // v1 + v2
    Vetor2D operator+(const Vetor2D& o) const {
        return {x + o.x, y + o.y};
    }
    // cout << v
    friend ostream& operator<<(ostream& os, const Vetor2D& v) {
        return os << "(" << v.x << ", " << v.y << ")";
    }
};
05
Templates — Programação Genérica
Funções e classes parametrizadas por tipo. A base da STL.
Intermédio
Template de função: template <typename T>
Template de classe
Especialização de template
Dedução automática de tipos
Variadic templates (C++11)
constexpr e metaprogramação básica
templates.cpp
// Uma função que funciona para qualquer tipo
template <typename T>
T maximo(T a, T b) { return a > b ? a : b; }

// Classe genérica — par de valores
template <typename A, typename B>
class Par {
public:
    A primeiro;  B segundo;
    Par(A a, B b) : primeiro(a), segundo(b) {}
};

int main() {
    cout << maximo(3, 7);        // int: 7
    cout << maximo(3.5, 2.1);   // double: 3.5
    Par<string, int> p("Alice", 20);
}
06
STL — Standard Template Library
vector, map, set, algoritmos e iteradores.
Intermédio
std::vector — array dinâmico
std::map e std::unordered_map
std::set e std::unordered_set
std::string e métodos
Iteradores: begin(), end(), auto
Algoritmos: sort, find, transform, for_each
Range-based for loop
std::pair e std::tuple
stl.cpp
#include <vector>
#include <algorithm>
#include <map>

vector<int> nums = {5, 2, 8, 1, 9, 3};

// Ordenar e iterar
sort(nums.begin(), nums.end());
for (auto n : nums) cout << n << " ";
// → 1 2 3 5 8 9

// Mapa: palavra → frequência
map<string, int> freq;
freq["hello"]++;
freq["world"]++;
freq["hello"]++;
cout << freq["hello"];  // 2
07
Gestão de Memória — Smart Pointers
unique_ptr, shared_ptr, weak_ptr. RAII e segurança de memória.
Avançado
Revisão: new e delete em C++
RAII — Resource Acquisition Is Initialization
unique_ptr — propriedade exclusiva
shared_ptr — propriedade partilhada
weak_ptr — evitar ciclos de referência
make_unique e make_shared
Move semantics: std::move
Rule of 5 em classes com memória
smart_ptrs.cpp
#include <memory>

// unique_ptr — libertado automaticamente
auto up = make_unique<Retangulo>(4.0, 3.0);
cout << up->area();  // 12 — sem new/delete!

// shared_ptr — contagem de referências
auto sp1 = make_shared<Circulo>(5.0);
auto sp2 = sp1;  // ambos apontam para o mesmo
cout << sp1.use_count();  // 2 referências
// Libertado quando ambos saem de scope
08
Exceções e Tratamento de Erros
try/catch/throw, hierarquias de exceção, noexcept.
Avançado
throw, try, catch
std::exception e subclasses
Criar exceções personalizadas
catch(...) — apanhar tudo
noexcept — garantir sem exceção
RAII como mecanismo de limpeza
excecoes.cpp
class ErroValor : public std::exception {
    string msg;
public:
    ErroValor(string m) : msg(m) {}
    const char* what() const noexcept override {
        return msg.c_str();
    }
};

double dividir(double a, double b) {
    if (b == 0) throw ErroValor("Divisão por zero");
    return a / b;
}

try {
    cout << dividir(10, 0);
} catch (const ErroValor& e) {
    cerr << "Erro: " << e.what() << "\n";
}
09
C++ Moderno — C++11/14/17/20
auto, lambda, range-for, structured bindings, concepts.
Avançado
auto — dedução de tipo
Lambda expressions: [capture](params){body}
std::optional, std::variant, std::any
Structured bindings: auto [a, b] = par
Constexpr if e fold expressions
std::filesystem, std::chrono
Concepts C++20 — restrições de template
Coroutines e ranges (C++20)
moderno.cpp
// Lambda — função anónima
auto quadrado = [](int x) { return x * x; };
cout << quadrado(5);  // 25

// Lambda com STL
vector<int> v = {1,2,3,4,5};
transform(v.begin(), v.end(), v.begin(),
    [](int x) { return x * x; });
// v = {1, 4, 9, 16, 25}

// Structured bindings (C++17)
map<string, int> notas = {{"Ana", 18}, {"Bob", 15}};
for (auto& [nome, nota] : notas)
    cout << nome << ": " << nota << "\n";
10
Ficheiros, Streams e I/O Avançado
fstream, stringstream, formatação avançada com iomanip.
Avançado
ifstream e ofstream
Ler e escrever ficheiros de texto e binários
stringstream — processar strings como streams
iomanip: setw, setprecision, fixed
Tratamento de erros de I/O
Serialização básica de objetos
ficheiros.cpp
#include <fstream>
#include <sstream>

// Escrever ficheiro
ofstream out("dados.txt");
out << "Ana 18\nBob 15\n";
out.close();

// Ler linha a linha e parsear
ifstream in("dados.txt");
string linha;
while (getline(in, linha)) {
    istringstream ss(linha);
    string nome; int nota;
    ss >> nome >> nota;
    cout << nome << " tem " << nota << "\n";
}
03 · Referência

💻 Referência Rápida — Sintaxe Essencial

Os padrões mais usados no dia a dia de qualquer programador C++.

Básico
Classes
STL
Herança
Smart Ptrs
Lambda
basico.cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    // Tipos básicos
    int    i = 42;
    double d = 3.14;
    bool   b = true;
    string s = "Olá C++";

    // Entrada e saída
    cout << s << "\n";
    string nome;
    cin >> nome;
    getline(cin, nome); // lê linha inteira

    // auto — dedução de tipo
    auto x = 3.14;  // x é double
    auto y = 42;    // y é int

    // Referências
    int  a = 10;
    int& r = a;  // r é alias de a
    r = 20;      // a agora vale 20

    // Iniciação uniforme (C++11)
    int v{5};       // equivalente a int v = 5;
    double w{3.14};

    return 0;
}
Especificadores de Formatação
ManipuladorEfeitoExemplo
setw(n)Largura mínimasetw(8) << 42 →      42
setprecision(n)Casas decimaissetprecision(2) << 3.14159 → 3.14
fixedNotação decimalfixed << 3.14 → 3.140000
left / rightAlinhamentoleft << setw(8) << "hi"
boolalphatrue/false em textoboolalpha << true → true
hex / octBase numéricahex << 255 → ff
ℹ️ Os manipuladores requerem #include <iomanip>. Muitos (como fixed e boolalpha) são persistentes — aplicam-se a todos os outputs seguintes.
classe_completa.cpp
class ContaBancaria {
    string titular;
    double saldo;
public:
    // Construtor com lista de inicialização
    ContaBancaria(string t, double s = 0.0)
        : titular(t), saldo(s) {}

    // Destrutor
    ~ContaBancaria() {
        cout << titular << ": conta encerrada\n";
    }

    // Método const — não modifica o objeto
    double getSaldo() const { return saldo; }

    bool levantar(double v) {
        if (v > saldo) return false;
        saldo -= v;
        return true;
    }

    void depositar(double v) { saldo += v; }

    // Sobrecarga de << como friend
    friend ostream& operator<<(ostream& os,
                               const ContaBancaria& c) {
        return os << c.titular << ": €" << c.saldo;
    }
};
utilizar.cpp
int main() {
    ContaBancaria c1("Ana", 500.0);
    ContaBancaria c2("Bob");  // saldo = 0

    c1.depositar(100.0);
    if (!c1.levantar(800.0))
        cout << "Saldo insuficiente!\n";

    cout << c1 << "\n";  // Ana: €600
    cout << c2 << "\n";  // Bob: €0
}
Ana: €600
Bob: €0
Bob: conta encerrada
Ana: conta encerrada
⚠️ Rule of 3/5/0: Se definires um destrutor, provavelmente precisas também de um copy constructor e copy assignment operator. Em C++ moderno, prefere a Rule of 0 — usa smart pointers e deixa o compilador gerar tudo.
stl_completo.cpp
#include <vector>
#include <map>
#include <set>
#include <algorithm>

// ── vector ──
vector<int> v = {3, 1, 4, 1, 5};
v.push_back(9);
v.pop_back();
cout << v.size() << "\n";   // 5
sort(v.begin(), v.end());

// ── map (ordenado por chave) ──
map<string, int> idades;
idades["Ana"] = 20;
idades["Bob"] = 18;
for (auto& [nome, idade] : idades)
    cout << nome << ": " << idade << "\n";

// ── set (sem duplicados) ──
set<int> s = {1, 2, 2, 3, 3};
cout << s.size(); // 3 — únicos

// ── Algoritmos ──
auto it = find(v.begin(), v.end(), 4);
int cnt = count(v.begin(), v.end(), 1); // 2
auto mx = *max_element(v.begin(), v.end());
Contêineres STL — Resumo
ContêinerAcessoInserçãoUso típico
vector<T>O(1)O(1) fimArray dinâmico
list<T>O(n)O(1)Inserções no meio
deque<T>O(1)O(1) ambosFila dupla
map<K,V>O(log n)O(log n)Dicionário ordenado
unordered_map<K,V>O(1) amort.O(1) amort.Dicionário rápido
set<T>O(log n)O(log n)Conjunto ordenado
stack<T>topoO(1)LIFO
queue<T>frenteO(1)FIFO
heranca.cpp
class Animal {
protected:
    string nome;
public:
    Animal(string n) : nome(n) {}
    virtual void falar() const = 0; // puro
    virtual ~Animal() = default;
    string getNome() const { return nome; }
};

class Cao : public Animal {
public:
    Cao(string n) : Animal(n) {}
    void falar() const override {
        cout << nome << ": Au-au!\n";
    }
};

class Gato : public Animal {
public:
    Gato(string n) : Animal(n) {}
    void falar() const override {
        cout << nome << ": Miau!\n";
    }
};

int main() {
    // Polimorfismo via ponteiro base
    vector<Animal*> animais;
    animais.push_back(new Cao("Rex"));
    animais.push_back(new Gato("Whiskers"));

    for (auto* a : animais) {
        a->falar();  // chama método correto!
        delete a;
    }
}
Rex: Au-au!
Whiskers: Miau!
⚠️ Destrutor virtual é obrigatório em classes base com métodos virtuais. Sem ele, ao fazer delete ptr_base o destrutor da classe derivada não é chamado — memory leak garantido.
Dica moderna: Prefere vector<unique_ptr<Animal>> a vector<Animal*> — elimina o new/delete manual e garante destruição correta.
smart_ptrs.cpp
#include <memory>

// unique_ptr — uma única dono
auto up = make_unique<Cao>("Rex");
up->falar();  // Rex: Au-au!
// Destruído automaticamente ao sair de scope

// Transferir propriedade
auto up2 = std::move(up);  // up fica nulo

// shared_ptr — vários donos
auto sp1 = make_shared<Gato>("Whiskers");
auto sp2 = sp1;  // ref count = 2
cout << sp1.use_count(); // 2
sp1.reset(); // ref count = 1
// Destruído quando sp2 sair de scope

// Polimorfismo com smart pointers
vector<unique_ptr<Animal>> animais;
animais.push_back(make_unique<Cao>("Buddy"));
animais.push_back(make_unique<Gato>("Luna"));
for (auto& a : animais) a->falar();
// Sem delete! Tudo gerido automaticamente.
Quando usar cada smart pointer
TipoUsoTransferível
unique_ptrPropriedade exclusiva. Usa por defeito.Sim (std::move)
shared_ptrPartilhado entre vários donos.Sim (cópia)
weak_ptrReferência sem propriedade — evita ciclos.Não é dono
lambdas.cpp
#include <functional>
#include <algorithm>

// Sintaxe: [capture](params) -> retorno { corpo }
auto soma = [](int a, int b) { return a + b; };
cout << soma(3, 4);  // 7

// Captura por valor
int base = 10;
auto somar_base = [base](int x) { return x + base; };

// Captura por referência
int cont = 0;
auto incrementar = [&cont]() { cont++; };
incrementar(); incrementar();
cout << cont; // 2

// Com STL — ordenar por comprimento
vector<string> palavras = {"banana", "kiwi", "maçã"};
sort(palavras.begin(), palavras.end(),
    [](const string& a, const string& b) {
        return a.size() < b.size();
    });
// kiwi, maçã, banana

// std::function — guardar lambdas
function<int(int)> fib = [&fib](int n) -> int {
    return n <= 1 ? n : fib(n-1) + fib(n-2);
};
Modos de Captura
SintaxeSignificado
[]Não captura nada
[=]Captura tudo por valor
[&]Captura tudo por referência
[x]Captura x por valor
[&x]Captura x por referência
[=, &x]Tudo por valor, exceto x por ref.
[this]Captura o ponteiro this
04 · Prática

🏋️ Exercícios de Treino

Pratica com estes desafios progressivos. Cada exercício tem dicas e solução completa — tenta resolver antes de ver!

📊 Progresso
0%0 / 10 exercícios concluídos100%
EX. 01
Olá, classe!
Cria uma classe Saudacao com um construtor que recebe um nome e um método dizer() que imprime uma mensagem personalizada.
classconstrutorcout
Iniciante
Objetivos
Dicas
Solução
Definir uma classe com membros privados e públicos
Usar um construtor com parâmetros
Criar um método const que não modifica o objeto
Instanciar objetos na stack
Dica 1: O membro string deve ser private e o método dizer() public.
Dica 2: Usa lista de inicialização no construtor: Saudacao(string n) : nome(n) {}
Dica 3: Marca o método como const pois não altera o estado do objeto.
ex01_saudacao.cpp
#include <iostream>
#include <string>
using namespace std;

class Saudacao {
    string nome;
public:
    Saudacao(string n) : nome(n) {}

    void dizer() const {
        cout << "Olá, " << nome << "! Bem-vindo ao C++.\n";
    }
};

int main() {
    Saudacao s1("Ana");
    Saudacao s2("Bob");
    s1.dizer();
    s2.dizer();
    return 0;
}
Olá, Ana! Bem-vindo ao C++.
Olá, Bob! Bem-vindo ao C++.
EX. 02
Calculadora com classes
Cria uma classe Calculadora com métodos somar, subtrair, multiplicar e dividir, com validação de divisão por zero via exceção.
classexceçõesmétodos
Iniciante
Objetivos
Dicas
Solução
Criar múltiplos métodos numa classe
Lançar exceções com throw
Apanhar exceções com try/catch
Usar std::invalid_argument
Dica 1: Todos os métodos devem ser const pois não modificam estado.
Dica 2: Inclui <stdexcept> para aceder a std::invalid_argument.
Dica 3: Testa com try { cout << c.dividir(5, 0); } catch(...) { ... }
ex02_calc.cpp
#include <iostream>
#include <stdexcept>
using namespace std;

class Calculadora {
public:
    double somar(double a, double b)       const { return a + b; }
    double subtrair(double a, double b)    const { return a - b; }
    double multiplicar(double a, double b) const { return a * b; }

    double dividir(double a, double b) const {
        if (b == 0.0)
            throw invalid_argument("Divisão por zero!");
        return a / b;
    }
};

int main() {
    Calculadora c;
    cout << c.somar(3, 4)       << "\n"; // 7
    cout << c.multiplicar(5, 6) << "\n"; // 30

    try {
        cout << c.dividir(10, 0) << "\n";
    } catch (const invalid_argument& e) {
        cerr << "Erro: " << e.what() << "\n";
    }
    return 0;
}
EX. 03
Vector de notas
Usa um std::vector<double> para armazenar notas. Calcula a média, o máximo e o mínimo. Lista as notas acima da média.
vectorSTLalgoritmos
Iniciante
Dicas
Solução
Dica 1: Usa *max_element(v.begin(), v.end()) para o máximo.
Dica 2: Para a média, acumula com for(auto n : notas) soma += n; e divide por notas.size().
Dica 3: Filtra acima da média com um simples if (n > media) no loop.
ex03_notas.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

int main() {
    vector<double> notas = {15.5, 18.0, 12.0, 9.5, 17.0, 14.0};

    double soma  = accumulate(notas.begin(), notas.end(), 0.0);
    double media = soma / notas.size();
    double mx    = *max_element(notas.begin(), notas.end());
    double mn    = *min_element(notas.begin(), notas.end());

    cout << "Média: "  << media << "\n";
    cout << "Máximo: " << mx    << "\n";
    cout << "Mínimo: " << mn    << "\n";
    cout << "Acima da média: ";
    for (double n : notas)
        if (n > media) cout << n << " ";
    cout << "\n";
    return 0;
}
Média: 14.3333
Máximo: 18
Mínimo: 9.5
Acima da média: 15.5 18 17
EX. 04
Hierarquia de Formas Geométricas
Cria uma classe abstrata Forma com método puro area(). Implementa Circulo, Retangulo e Triangulo. Usa um vector de ponteiros base para polimorfismo.
herançapolimorfismovirtualabstrato
Intermédio
Dicas
Solução
Dica 1: Declara virtual double area() const = 0; na classe base para a tornar abstrata.
Dica 2: Usa vector<unique_ptr<Forma>> com make_unique<Circulo>(raio).
Dica 3: O destrutor da classe base deve ser virtual ~Forma() = default;
ex04_formas.cpp
#include <iostream>
#include <vector>
#include <memory>
#include <cmath>
using namespace std;

class Forma {
public:
    virtual double area()     const = 0;
    virtual string tipo()     const = 0;
    virtual ~Forma() = default;

    void info() const {
        cout << tipo() << ": área = "
             << area() << "\n";
    }
};

class Circulo : public Forma {
    double r;
public:
    Circulo(double r) : r(r) {}
    double area() const override { return M_PI * r * r; }
    string tipo() const override { return "Círculo"; }
};

class Retangulo : public Forma {
    double l, a;
public:
    Retangulo(double l, double a) : l(l), a(a) {}
    double area() const override { return l * a; }
    string tipo() const override { return "Retângulo"; }
};

int main() {
    vector<unique_ptr<Forma>> formas;
    formas.push_back(make_unique<Circulo>(5.0));
    formas.push_back(make_unique<Retangulo>(4.0, 3.0));

    for (auto& f : formas) f->info();
    return 0;
}
Círculo: área = 78.5398
Retângulo: área = 12
EX. 05
Contador de palavras com map
Lê uma string, divide-a em palavras e usa std::map<string,int> para contar a frequência de cada palavra. Apresenta as palavras por ordem de frequência (descendente).
mapstringalgoritmoslambda
Intermédio
Dicas
Solução
Dica 1: Usa istringstream ss(texto); string palavra; while(ss >> palavra) para dividir.
Dica 2: Copia o map para um vector<pair<string,int>> e ordena com lambda: sort(..., [](auto& a, auto& b){ return a.second > b.second; })
ex05_palavras.cpp
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    string texto = "o rato roeu a roupa do rei de roma o rato";
    map<string, int> freq;

    istringstream ss(texto);
    string palavra;
    while (ss >> palavra) freq[palavra]++;

    // Ordenar por frequência
    vector<pair<string,int>> v(freq.begin(), freq.end());
    sort(v.begin(), v.end(),
        [](auto& a, auto& b){ return a.second > b.second; });

    for (auto& [p, c] : v)
        cout << p << ": " << c << "\n";
    return 0;
}
o: 3 · rato: 2 · a: 1 · de: 1 · do: 1 · rei: 1 ...
EX. 06
Sobrecarga de operadores — Vetor 3D
Implementa uma classe Vetor3D com sobrecarga de +, -, produto escalar (*), norma e operador << para output.
operator overloadmathfriend
Intermédio
Dicas
Solução
Dica 1: Produto escalar: a.x*b.x + a.y*b.y + a.z*b.z. Retorna double.
Dica 2: Norma: sqrt(x*x + y*y + z*z). Inclui <cmath>.
Dica 3: O operator<< deve ser friend para aceder a membros privados.
ex06_vetor3d.cpp
#include <iostream>
#include <cmath>
using namespace std;

class Vetor3D {
public:
    double x, y, z;
    Vetor3D(double x, double y, double z) : x(x), y(y), z(z) {}

    Vetor3D operator+(const Vetor3D& o) const
        { return {x+o.x, y+o.y, z+o.z}; }
    Vetor3D operator-(const Vetor3D& o) const
        { return {x-o.x, y-o.y, z-o.z}; }

    // Produto escalar
    double operator*(const Vetor3D& o) const
        { return x*o.x + y*o.y + z*o.z; }

    double norma() const
        { return sqrt(x*x + y*y + z*z); }

    friend ostream& operator<<(ostream& os, const Vetor3D& v) {
        return os << "(" << v.x << ", " << v.y << ", " << v.z << ")";
    }
};

int main() {
    Vetor3D a(1,2,3), b(4,5,6);
    cout << "a + b = " << (a+b) << "\n"; // (5, 7, 9)
    cout << "a · b = " << (a*b) << "\n"; // 32
    cout << "|a|  = " << a.norma() << "\n"; // 3.742
}
EX. 07
Template de Stack genérica
Implementa uma classe template Stack<T> com operações push, pop, peek, isEmpty e size. Usa internamente um vector<T>.
templateclassvectorexceções
Avançado
Dicas
Solução
Dica 1: template <typename T> class Stack { vector<T> dados; ... }
Dica 2: pop() e peek() devem lançar runtime_error se a stack estiver vazia.
Dica 3: Testa com vários tipos: Stack<int>, Stack<string>, Stack<double>.
ex07_stack.cpp
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

template <typename T>
class Stack {
    vector<T> dados;
public:
    void push(const T& v) { dados.push_back(v); }

    T pop() {
        if (isEmpty()) throw runtime_error("Stack vazia");
        T v = dados.back();
        dados.pop_back();
        return v;
    }

    const T& peek() const {
        if (isEmpty()) throw runtime_error("Stack vazia");
        return dados.back();
    }

    bool   isEmpty() const { return dados.empty(); }
    size_t size()    const { return dados.size(); }
};

int main() {
    Stack<int> s;
    s.push(1); s.push(2); s.push(3);
    cout << s.pop() << "\n";   // 3
    cout << s.peek() << "\n";  // 2
    cout << s.size() << "\n";  // 2
    return 0;
}
EX. 08
Smart Pointers — Lista Ligada
Implementa uma lista ligada simples usando unique_ptr para gestão automática de memória. Operações: inserir no início, imprimir e contar elementos.
unique_ptrstructRAII
Avançado
Dicas
Solução
Dica 1: struct No { int val; unique_ptr<No> prox; }; — o destrutor automático liberta recursivamente.
Dica 2: Para inserir no início: cria novo nó, faz novo->prox = std::move(head), depois head = std::move(novo).
ex08_lista.cpp
#include <iostream>
#include <memory>
using namespace std;

struct No {
    int val;
    unique_ptr<No> prox;
    No(int v) : val(v) {}
};

class Lista {
    unique_ptr<No> head;
public:
    void inserir(int v) {
        auto novo = make_unique<No>(v);
        novo->prox = std::move(head);
        head = std::move(novo);
    }

    void imprimir() const {
        const No* p = head.get();
        while (p) { cout << p->val << " → "; p = p->prox.get(); }
        cout << "null\n";
    }
};

int main() {
    Lista l;
    l.inserir(3); l.inserir(2); l.inserir(1);
    l.imprimir(); // 1 → 2 → 3 → null
    // Sem delete! unique_ptr gere tudo.
}
EX. 09
Pipeline funcional com lambdas
Cria uma função pipeline que aplica sequencialmente uma lista de transformações (vector<function<int(int)>>) a um valor. Demonstra composição funcional.
lambdafunctionfunctionalC++17
Avançado
Dicas
Solução
Dica 1: A assinatura de pipeline: int pipeline(int v, vector<function<int(int)>> fns)
Dica 2: Itera sobre as funções: for (auto& f : fns) v = f(v); e retorna o resultado.
ex09_pipeline.cpp
#include <iostream>
#include <vector>
#include <functional>
using namespace std;

int pipeline(int v, vector<function<int(int)>> fns) {
    for (auto& f : fns) v = f(v);
    return v;
}

int main() {
    auto resultado = pipeline(3, {
        [](int x) { return x * x; },      // 3 → 9
        [](int x) { return x + 1; },      // 9 → 10
        [](int x) { return x * 2; },      // 10 → 20
        [](int x) { return x - 5; }       // 20 → 15
    });
    cout << resultado << "\n"; // 15
    return 0;
}
15
EX. 10
Matriz com templates e iteradores
Cria uma classe template Matriz<T,R,C> com operações de acesso, soma, transposição e impressão formatada. Usa arrays internos de tamanho fixo (sem heap).
templatearrayoperator[]C++17
Avançado
Dicas
Solução
Dica 1: Usa T dados[R][C]; como membro privado para armazenamento estático sem heap.
Dica 2: O operator()(int r, int c) é mais idiomático que operator[][] para matrizes em C++.
Dica 3: Transposição retorna Matriz<T,C,R> — nota a inversão dos parâmetros de template!
ex10_matriz.cpp
#include <iostream>
#include <iomanip>
using namespace std;

template <typename T, int R, int C>
class Matriz {
    T dados[R][C]{};
public:
    T& operator()(int r, int c)      { return dados[r][c]; }
    T  operator()(int r, int c) const { return dados[r][c]; }

    Matriz<T,R,C> operator+(const Matriz<T,R,C>& o) const {
        Matriz<T,R,C> res;
        for (int i=0; i<R; i++)
            for (int j=0; j<C; j++)
                res(i,j) = dados[i][j] + o(i,j);
        return res;
    }

    Matriz<T,C,R> transposta() const {
        Matriz<T,C,R> res;
        for (int i=0; i<R; i++)
            for (int j=0; j<C; j++)
                res(j,i) = dados[i][j];
        return res;
    }

    void imprimir() const {
        for (int i=0; i<R; i++) {
            for (int j=0; j<C; j++)
                cout << setw(4) << dados[i][j];
            cout << "\n";
        }
    }
};
05 · Projetos

🏆 Desafios de Projeto

Projetos maiores que combinam múltiplos conceitos. Ideais para o portfólio.

DESAFIO 01 · Intermédio
🏦 Sistema Bancário
Cria classes Conta, ContaPoupanca e ContaCorrente com herança. Implementa transferências, extrato e juros. Usa shared_ptr para gerir contas partilhadas entre clientes.
herançashared_ptrpolimorfismo
Começar →
DESAFIO 02 · Intermédio
📚 Biblioteca de Livros
Sistema de gestão de biblioteca com classes Livro, Utilizador e Biblioteca. Requisitos: pesquisa por título/autor, empréstimos com prazo, persistência em ficheiro CSV.
STLfstreammap
Começar →
DESAFIO 03 · Avançado
🎮 Jogo da Memória CLI
Jogo da memória para terminal. Tabuleiro parametrizável, classes Carta e Tabuleiro, registo de pontuação, modo a 2 jogadores. Usa unique_ptr para as cartas.
unique_ptrtemplatevector
Começar →
DESAFIO 04 · Avançado
🔢 Calculadora de Matrizes
Implementa uma calculadora de álgebra linear com classe Matrix genérica. Operações: soma, produto, determinante, inversa, eliminação de Gauss. Interface de linha de comandos interativa.
templateoperator overloadalgoritmos
Começar →
DESAFIO 05 · Avançado
🕸️ Grafo e BFS/DFS
Implementa um grafo não-dirigido com lista de adjacências usando unordered_map. Algoritmos: BFS, DFS, caminho mais curto (Dijkstra). Leitura do grafo a partir de ficheiro.
unordered_mapqueuepriority_queue
Começar →
DESAFIO 06 · Expert
⚡ Motor de Eventos
Implementa um sistema de eventos (Observer Pattern) com EventBus genérico usando templates e function. Subscritores, publicadores, filtros por tipo. Aplica ao design de jogos.
design patternfunctiontemplateC++17
Começar →
06 · Ferramentas

🌐 Recursos e Compiladores

Ferramentas, referências e comunidades para continuar a aprender.

🚀 Compilar localmente: Instala g++ com sudo apt install g++ (Linux/WSL) ou usa o MSVC no Visual Studio (Windows). Compila sempre com g++ -std=c++17 -Wall -Wextra ficheiro.cpp -o programa para ativar todos os avisos.