UC02839

C/C++ avançado

Templates, STL, smart pointers, concorrência

Curso profissional · 50h · Linguagens de sistema

Plano

  1. Revisão e C++ moderno
  2. Templates e generics
  3. STL containers e algoritmos
  4. Smart pointers e RAII
  5. Lambdas e funcional
  6. Exceptions
  7. Concorrência (threads)
  8. Move semantics, otimização
  9. Build e testing modernos

Bloco 1 · C++ moderno

Por que C++ moderno

  • C++11 (2011) mudou a linguagem: auto, lambdas, smart pointers, threads.
  • C++14/17/20/23: ainda mais expressivo.
  • Pré-requisitos: dominar C básico (UC00669).
g++ -std=c++20 -Wall -Wextra programa.cpp -o programa

auto, range-for, init

auto x = 42;           // int
auto pi = 3.14;        // double
auto nome = "Ana";     // const char*

// Inicialização uniforme {}
vector<int> nums{1, 2, 3, 4, 5};
Pessoa p{"Ana", 25};

// Range-based for
for (auto n : nums) {
    cout << n << " ";
}

// Estruturado binding (C++17)
auto [chave, valor] = par;

Bloco 2 · Templates

Function templates

template <typename T>
T maximo(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    cout << maximo(3, 7) << endl;        // 7 (int)
    cout << maximo(2.5, 1.8) << endl;    // 2.5 (double)
    cout << maximo<string>("ana", "bia") << endl;  // bia
}

Templates = código genérico, instanciado em compile-time.

Class templates

template <typename T>
class Caixa {
private:
    T conteudo;
    
public:
    Caixa(T val) : conteudo(val) {}
    T obter() const { return conteudo; }
    void definir(T val) { conteudo = val; }
};

int main() {
    Caixa<int> c1(42);
    Caixa<string> c2("Olá");
    
    cout << c1.obter() << endl;   // 42
    cout << c2.obter() << endl;   // Olá
}

Variadic templates

template <typename T>
void imprimir(T arg) {
    cout << arg << endl;
}

template <typename T, typename... Args>
void imprimir(T primeiro, Args... resto) {
    cout << primeiro << " ";
    imprimir(resto...);
}

imprimir(1, "Ana", 3.14, true);
// 1 Ana 3.14 1

... = parameter pack (varios argumentos de tipos diferentes).

Bloco 3 · STL

Containers principais

#include <vector>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <deque>
#include <array>

vector<int> v{1, 2, 3};          // array dinâmico
list<int> l{1, 2, 3};            // lista ligada
deque<int> d{1, 2, 3};           // double-ended queue
array<int, 5> a{1, 2, 3, 4, 5};  // fixed array

map<string, int> m;              // árvore balanceada (ordenado)
unordered_map<string, int> um;   // hash table (rápido)

set<int> s;                      // únicos, ordenados
unordered_set<int> us;           // únicos, hash

vector — o mais usado

vector<int> v;

v.push_back(1);
v.push_back(2);
v.push_back(3);

v.size();        // 3
v.empty();       // false
v.front();       // 1
v.back();        // 3
v[0];            // 1
v.at(0);         // 1 (com bounds check)

v.pop_back();    // remove último
v.clear();       // esvazia

v.reserve(100);  // reserva capacidade (evita realloc)

// Range-based for
for (auto& n : v) {
    n *= 2;
}

map / unordered_map

map<string, int> idades;
idades["Ana"] = 25;
idades["Bruno"] = 30;

// Acesso
int idadeAna = idades["Ana"];
if (idades.contains("Carla")) { /* C++20 */ }

// Iterar (com structured binding)
for (const auto& [nome, idade] : idades) {
    cout << nome << ": " << idade << endl;
}

// Remover
idades.erase("Ana");

map = ordenado (árvore), O(log n).
unordered_map = hash, O(1) amortizado.

Algorithms

#include <algorithm>
#include <numeric>

vector<int> v{3, 1, 4, 1, 5, 9, 2, 6};

// Ordenar
sort(v.begin(), v.end());                     // ascending
sort(v.begin(), v.end(), greater<int>());     // descending

// Pesquisar
auto it = find(v.begin(), v.end(), 5);
if (it != v.end()) { /* encontrou */ }

// Counting
int n = count(v.begin(), v.end(), 1);

// Transformar
transform(v.begin(), v.end(), v.begin(), 
    [](int n) { return n * 2; });

// Acumular
int soma = accumulate(v.begin(), v.end(), 0);

// Min/max
auto [mn, mx] = minmax_element(v.begin(), v.end());

Bloco 4 · Smart pointers

Problema com raw pointers

void problema() {
    Pessoa* p = new Pessoa("Ana");
    // ... 100 linhas
    if (algo) return;  // LEAK!
    
    delete p;  // só se chegar aqui
}

Solução: smart pointers que se auto-deletam (RAII).

unique_ptr

#include <memory>

void exemplo() {
    auto p = make_unique<Pessoa>("Ana", 25);
    p->apresentar();
    
    // No fim do scope, ~unique_ptr() chama delete automaticamente
}

// Mover (não copiar)
auto p2 = move(p);   // p agora é nullptr

unique_ptr = ownership único, sem cópia.

shared_ptr e weak_ptr

auto p1 = make_shared<Pessoa>("Ana", 25);
auto p2 = p1;   // partilhar — incrementa refcount
auto p3 = p1;

cout << p1.use_count() << endl;  // 3

// ~Pessoa chamado quando o último shared_ptr morre

weak_ptr = referência fraca, evita ciclos:

shared_ptr<Pessoa> sp = make_shared<Pessoa>("Ana");
weak_ptr<Pessoa> wp = sp;

if (auto locked = wp.lock()) {  // converte para shared_ptr
    locked->apresentar();
}

Bloco 5 · Lambdas

Sintaxe

auto somar = [](int a, int b) { return a + b; };
cout << somar(2, 3) << endl;  // 5

// Capture by value
int x = 10;
auto add_x = [x](int n) { return n + x; };

// Capture by reference
auto incr = [&x]() { x++; };

// Capture all
auto f = [=](int n) { return n + x; };   // tudo por valor
auto g = [&](int n) { x = n; };          // tudo por ref

Lambdas com STL

vector<int> v{1, 2, 3, 4, 5, 6};

// Filter
vector<int> pares;
copy_if(v.begin(), v.end(), back_inserter(pares),
    [](int n) { return n % 2 == 0; });

// Sort customizado
vector<string> nomes{"Carla", "Ana", "Bruno"};
sort(nomes.begin(), nomes.end(),
    [](const string& a, const string& b) {
        return a.length() < b.length();
    });

// for_each
for_each(v.begin(), v.end(),
    [](int n) { cout << n << " "; });

Bloco 6 · Exceptions

Try/catch

#include <stdexcept>

try {
    if (idade < 0)
        throw invalid_argument("Idade negativa");
    if (idade > 150)
        throw out_of_range("Idade demasiado alta");
}
catch (const invalid_argument& e) {
    cerr << "Argumento inválido: " << e.what() << endl;
}
catch (const exception& e) {
    cerr << "Erro: " << e.what() << endl;
}

Custom exception

class SaldoInsuficiente : public runtime_error {
public:
    double saldo;
    
    SaldoInsuficiente(double s, const string& msg)
        : runtime_error(msg), saldo(s) {}
};

void sacar(double valor) {
    if (valor > saldo)
        throw SaldoInsuficiente(saldo, "Sem fundos");
}

try {
    sacar(1000);
} catch (const SaldoInsuficiente& e) {
    cerr << e.what() << " — saldo " << e.saldo << endl;
}

Bloco 7 · Threads

std::thread

#include <thread>

void trabalho(int id) {
    cout << "Thread " << id << " a correr" << endl;
}

int main() {
    thread t1(trabalho, 1);
    thread t2(trabalho, 2);
    
    t1.join();  // espera t1 acabar
    t2.join();
    
    cout << "Fim" << endl;
}

Mutex

#include <mutex>

mutex m;
int contador = 0;

void incrementar() {
    for (int i = 0; i < 1000; i++) {
        lock_guard<mutex> lock(m);
        contador++;
    }
}

int main() {
    thread t1(incrementar);
    thread t2(incrementar);
    t1.join();
    t2.join();
    cout << contador << endl;  // 2000 (garantido com lock)
}

lock_guard = RAII para mutex.

async / future

#include <future>

int trabalho_pesado() {
    this_thread::sleep_for(chrono::seconds(2));
    return 42;
}

int main() {
    auto futuro = async(launch::async, trabalho_pesado);
    
    cout << "A trabalhar..." << endl;
    
    int resultado = futuro.get();   // bloqueia até ter resultado
    cout << "Resultado: " << resultado << endl;
}

Bloco 8 · Move semantics

Rvalue references e std::move

vector<int> a{1, 2, 3, 4, 5};

vector<int> b = a;             // COPY (lento)
vector<int> c = move(a);       // MOVE (rápido — só transfere pointer)
// a agora vazio

Move evita cópias desnecessárias — essencial para performance.

Move constructor

class MeuVetor {
    int* dados;
    size_t tamanho;
    
public:
    // Move constructor
    MeuVetor(MeuVetor&& outro) noexcept
        : dados(outro.dados), tamanho(outro.tamanho)
    {
        outro.dados = nullptr;
        outro.tamanho = 0;
    }
    
    // Move assignment
    MeuVetor& operator=(MeuVetor&& outro) noexcept {
        if (this != &outro) {
            delete[] dados;
            dados = outro.dados;
            tamanho = outro.tamanho;
            outro.dados = nullptr;
            outro.tamanho = 0;
        }
        return *this;
    }
};

Bloco 9 · Build moderno

CMake

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)
project(MeuProjecto LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(programa
    src/main.cpp
    src/utils.cpp
)

target_include_directories(programa PRIVATE include)

Build:

cmake -B build
cmake --build build
./build/programa

Testes com Catch2

#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include "calculadora.h"

TEST_CASE("Somar funciona") {
    Calculadora c;
    REQUIRE(c.somar(2, 3) == 5);
    REQUIRE(c.somar(0, 0) == 0);
}

TEST_CASE("Dividir por zero lança") {
    Calculadora c;
    REQUIRE_THROWS_AS(c.dividir(10, 0), invalid_argument);
}

UC02839 · resumo

  • C++ moderno (C++11+) = transformação completa da linguagem.
  • auto, range-for, lambdas = código compacto e expressivo.
  • Templates = código genérico em compile-time.
  • STL = vector, map, algorithm — tudo pronto, optimizado.
  • Smart pointers = adeus memory leaks (RAII).
  • std::thread + mutex para concorrência.
  • Move semantics = performance sem cópias.
  • CMake + Catch2 = build e testes modernos.
  • Base para Unreal Engine, sistemas embedded sérios, trading.