Folha de Dicas de C++

Aprenda C++ com Laboratórios Práticos

Aprenda programação C++ através de laboratórios práticos e cenários do mundo real. O LabEx oferece cursos abrangentes de C++ cobrindo sintaxe essencial, programação orientada a objetos, contêineres STL, gerenciamento de memória e técnicas avançadas. Domine os recursos poderosos do C++ para construir aplicações de alto desempenho e software de sistema.

Sintaxe Básica e Estrutura

Programa Olá Mundo

Estrutura básica de um programa C++.

#include <iostream>
using namespace std;
int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

Headers e Namespaces

Incluir bibliotecas e gerenciar namespaces.

#include <iostream>  // Fluxo de entrada/saída
#include <vector>    // Arrays dinâmicos
#include <string>    // Classe string
#include <algorithm> // Algoritmos STL
using namespace std;
// Ou especificar individualmente:
// using std::cout;
// using std::cin;

Comentários

Comentários de linha única e múltiplas linhas.

// Comentário de linha única
/*
Comentário de
múltiplas linhas
que se estende por várias linhas
*/
// TODO: Implementar funcionalidade
/* FIXME: Bug nesta seção */

Função Main

Ponto de entrada do programa com valores de retorno.

int main() {
    // Código do programa aqui
    return 0;  // Sucesso
}
int main(int argc, char* argv[]) {
    // argc: contagem de argumentos
    // argv: valores dos argumentos (linha de comando)
    return 0;
}
Quiz

Faça login para responder este quiz e acompanhar seu progresso de aprendizagem

Qual é a diferença entre as instruções de saída C e C++?
Não há diferença
C usa printf(), C++ usa cout com o operador <<
C++ não suporta saída
C usa cout, C++ usa printf

Saída Básica

Exibir texto e variáveis no console.

cout << "Hello" << endl;
cout << "Value: " << 42 << endl;
// Múltiplos valores em uma linha
cout << "Name: " << name << ", Age: " << age << endl;

Entrada Básica

Ler a entrada do usuário pelo console.

int age;
string name;
cin >> age;
cin >> name;
// Ler linha inteira com espaços
getline(cin, name);

Tipos de Dados e Variáveis

Tipos Primitivos

Tipos de dados básicos para armazenar diferentes tipos de valores.

// Tipos inteiros
int age = 25;
short small_num = 100;
long large_num = 1000000L;
long long huge_num = 9223372036854775807LL;
// Tipos de ponto flutuante
float price = 19.99f;
double precise = 3.14159265359;
// Caractere e booleano
char grade = 'A';
bool is_valid = true;

String e Arrays

Tipos de dados de texto e coleção.

// Strings
string name = "John Doe";
string empty_str;
// Arrays
int numbers[5] = {1, 2, 3, 4, 5};
int matrix[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
// Arrays dinâmicos (vetores)
vector<int> dynamic_array = {10, 20, 30};
vector<string> names(5); // Tamanho 5, strings vazias
Quiz

Faça login para responder este quiz e acompanhar seu progresso de aprendizagem

Qual é a principal vantagem do vector sobre arrays regulares em C++?
Vectors são mais rápidos
Vectors podem redimensionar dinamicamente, enquanto arrays têm tamanho fixo
Vectors usam menos memória
Não há vantagem

Constantes e Auto

Valores imutáveis e dedução automática de tipo.

// Constantes
const int MAX_SIZE = 100;
const double PI = 3.14159;
// Palavra-chave Auto (C++11+)
auto x = 42;        // int
auto y = 3.14;      // double
auto name = "John"; // const char*
// Alias de tipo
typedef unsigned int uint;
using real = double;

Estruturas de Controle de Fluxo

Declarações Condicionais

Tomar decisões com base em condições.

// Declaração If-else
if (age >= 18) {
    cout << "Adulto" << endl;
} else if (age >= 13) {
    cout << "Adolescente" << endl;
} else {
    cout << "Criança" << endl;
}
// Operador ternário
string status = (age >= 18) ? "Adulto" : "Menor";
// Declaração Switch
switch (grade) {
    case 'A':
        cout << "Excelente!" << endl;
        break;
    case 'B':
        cout << "Bom trabalho!" << endl;
        break;
    default:
        cout << "Continue tentando!" << endl;
}

Loops For

Iterar com loops baseados em contador.

// Loop for tradicional
for (int i = 0; i < 10; i++) {
    cout << i << " ";
}
// Loop for baseado em intervalo (C++11+)
vector<int> numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
    cout << num << " ";
}
// Auto com loop baseado em intervalo
for (auto& item : container) {
    // Processar item
}
Quiz

Faça login para responder este quiz e acompanhar seu progresso de aprendizagem

O que é um loop for baseado em intervalo em C++?
Um loop que só funciona com arrays
Um loop que itera sobre todos os elementos em um contêiner automaticamente
Um loop que roda para sempre
Um loop que requer gerenciamento manual de índice

Loops While

Iteração baseada em condição.

// Loop While
int count = 0;
while (count < 5) {
    cout << count << endl;
    count++;
}
// Loop Do-while (executa pelo menos uma vez)
int input;
do {
    cout << "Digite um número (0 para sair): ";
    cin >> input;
} while (input != 0);

Controle de Loop

Declarações break e continue.

for (int i = 0; i < 10; i++) {
    if (i == 3) {
        continue; // Pular iteração
    }
    if (i == 7) {
        break;    // Sair do loop
    }
    cout << i << " ";
}
// Loops aninhados com break rotulado
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == j) break; // Quebra apenas o loop interno
        cout << i << "," << j << " ";
    }
}

Funções

Declaração e Definição de Função

Criar blocos de código reutilizáveis.

// Declaração de função (protótipo)
int add(int a, int b);
void printMessage(string msg);
// Definição de função
int add(int a, int b) {
    return a + b;
}
void printMessage(string msg) {
    cout << msg << endl;
}
// Chamada de função
int result = add(5, 3);
printMessage("Olá, funções!");

Sobrecarga de Função (Function Overloading)

Múltiplas funções com o mesmo nome.

// Tipos de parâmetros diferentes
int multiply(int a, int b) {
    return a * b;
}
double multiply(double a, double b) {
    return a * b;
}
// Número diferente de parâmetros
int multiply(int a, int b, int c) {
    return a * b * c;
}

Parâmetros Padrão

Fornecer valores padrão para parâmetros de função.

void greet(string name, string greeting = "Hello") {
    cout << greeting << ", " << name << "!" << endl;
}
// Chamadas de função
greet("Alice");              // Usa o padrão "Hello"
greet("Bob", "Good morning"); // Usa saudação personalizada

Passagem por Referência

Modificar variáveis através de parâmetros de função.

// Passagem por valor (cópia)
void changeValue(int x) {
    x = 100; // Variável original inalterada
}
// Passagem por referência
void changeReference(int& x) {
    x = 100; // Variável original modificada
}
// Referência const (somente leitura, eficiente)
void processLargeData(const vector<int>& data) {
    // Pode ler dados, mas não modificar
}

Programação Orientada a Objetos

Definição de Classe

Definir tipos de dados personalizados com atributos e métodos.

class Rectangle {
private:
    double width, height;
public:
    // Construtor
    Rectangle(double w, double h) : width(w), height(h) {}

    // Construtor padrão
    Rectangle() : width(0), height(0) {}

    // Funções membro
    double area() const {
        return width * height;
    }

    void setDimensions(double w, double h) {
        width = w;
        height = h;
    }

    // Funções getter
    double getWidth() const { return width; }
    double getHeight() const { return height; }
};

Criação e Uso de Objeto

Instanciar e usar objetos de classe.

// Criar objetos
Rectangle rect1(5.0, 3.0);
Rectangle rect2; // Construtor padrão
// Usar funções membro
cout << "Area: " << rect1.area() << endl;
rect2.setDimensions(4.0, 2.0);
// Alocação dinâmica
Rectangle* rect3 = new Rectangle(6.0, 4.0);
cout << rect3->area() << endl;
delete rect3; // Limpar memória

Herança

Criar classes especializadas a partir de classes base.

class Shape {
protected:
    string color;

public:
    Shape(string c) : color(c) {}
    virtual double area() const = 0; // Virtual puro
    string getColor() const { return color; }
};
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r, string c) : Shape(c), radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }
};

Polimorfismo

Usar ponteiros de classe base para acessar objetos derivados.

// Funções virtuais e polimorfismo
vector<Shape*> shapes;
shapes.push_back(new Circle(5.0, "red"));
shapes.push_back(new Rectangle(4.0, 6.0));
for (Shape* shape : shapes) {
    cout << "Area: " << shape->area() << endl;
    // Chama o método da classe derivada apropriado
}

Gerenciamento de Memória

Alocação Dinâmica de Memória

Alocar e desalocar memória em tempo de execução.

// Objeto único
int* ptr = new int(42);
cout << *ptr << endl;
delete ptr;
ptr = nullptr;
// Alocação de Array
int* arr = new int[10];
for (int i = 0; i < 10; i++) {
    arr[i] = i * i;
}
delete[] arr;
// Verificar falha de alocação
int* large_array = new(nothrow) int[1000000];
if (large_array == nullptr) {
    cout << "Alocação falhou!" << endl;
}

Ponteiros Inteligentes (Smart Pointers) (C++11+)

Gerenciamento automático de memória com RAII.

#include <memory>
// unique_ptr (propriedade exclusiva)
unique_ptr<int> ptr1 = make_unique<int>(42);
unique_ptr<int> ptr2 = move(ptr1); // Transfere a propriedade
// shared_ptr (propriedade compartilhada)
shared_ptr<int> sptr1 = make_shared<int>(100);
shared_ptr<int> sptr2 = sptr1; // Compartilha a propriedade
cout << sptr1.use_count() << endl; // Contagem de referências

Referências vs Ponteiros

Duas maneiras de acessar objetos indiretamente.

int x = 10;
// Referência (alias)
int& ref = x;  // Deve ser inicializada
ref = 20;      // Altera x para 20
// Ponteiro
int* ptr = &x; // Aponta para o endereço de x
*ptr = 30;     // Desreferencia e altera x
ptr = nullptr; // Pode apontar para nada
// Variações const
const int* ptr1 = &x;    // Não pode mudar o valor
int* const ptr2 = &x;    // Não pode mudar o endereço
const int* const ptr3 = &x; // Não pode mudar nenhum dos dois

Pilha vs Heap

Estratégias de alocação de memória.

// Alocação na Pilha (automática)
int stack_var = 42;
int stack_array[100];
// Alocação no Heap (dinâmica)
int* heap_var = new int(42);
int* heap_array = new int[100];
// Objetos da pilha são limpos automaticamente
// Objetos do heap devem ser deletados manualmente
delete heap_var;
delete[] heap_array;

Biblioteca Padrão de Templates (STL)

Contêineres: Vector e String

Arrays dinâmicos e manipulação de strings.

#include <vector>
#include <string>
// Operações com Vector
vector<int> nums = {1, 2, 3};
nums.push_back(4);        // Adicionar elemento
nums.pop_back();          // Remover o último
nums.insert(nums.begin() + 1, 10); // Inserir na posição
nums.erase(nums.begin()); // Remover o primeiro
// Operações com String
string text = "Hello";
text += " World";         // Concatenação
text.append("!");         // Anexar
cout << text.substr(0, 5) << endl; // Substring
text.replace(6, 5, "C++"); // Substituir "World" por "C++"

Contêineres: Map e Set

Contêineres associativos para pares chave-valor e elementos únicos.

#include <map>
#include <set>
// Map (pares chave-valor)
map<string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
ages.insert({"Charlie", 35});
// Set (elementos únicos)
set<int> unique_nums = {3, 1, 4, 1, 5, 9};
unique_nums.insert(2);
unique_nums.erase(1);
// Automaticamente ordenado: {2, 3, 4, 5, 9}

Algoritmos

Algoritmos STL para operações comuns.

#include <algorithm>
vector<int> nums = {64, 34, 25, 12, 22, 11, 90};
// Ordenação
sort(nums.begin(), nums.end());
sort(nums.rbegin(), nums.rend()); // Ordenação reversa
// Busca
auto it = find(nums.begin(), nums.end(), 25);
if (it != nums.end()) {
    cout << "Encontrado na posição: " << it - nums.begin();
}
// Outros algoritmos úteis
reverse(nums.begin(), nums.end());
int max_val = *max_element(nums.begin(), nums.end());
int count = count_if(nums.begin(), nums.end(),
                    [](int x) { return x > 50; });

Iteradores

Navegar pelos contêineres de forma eficiente.

vector<string> words = {"hello", "world", "cpp"};
// Tipos de iterador
vector<string>::iterator it;
auto it2 = words.begin(); // Auto C++11
// Iterar pelo contêiner
for (it = words.begin(); it != words.end(); ++it) {
    cout << *it << " ";
}
// Loop baseado em intervalo (preferido)
for (const auto& word : words) {
    cout << word << " ";
}

Operações de Entrada/Saída

Entrada de Arquivo: Leitura de Arquivos

Ler dados de arquivos de texto.

#include <fstream>
#include <sstream>
// Ler arquivo inteiro
ifstream file("data.txt");
if (file.is_open()) {
    string line;
    while (getline(file, line)) {
        cout << line << endl;
    }
    file.close();
}
// Ler palavra por palavra
ifstream file2("numbers.txt");
int number;
while (file2 >> number) {
    cout << number << " ";
}
// Leitura com verificação de erro
if (!file.good()) {
    cerr << "Erro ao ler o arquivo!" << endl;
}

Processamento de Fluxo de String (String Stream)

Analisar e manipular strings como fluxos.

#include <sstream>
// Analisar valores separados por vírgula
string data = "apple,banana,cherry";
stringstream ss(data);
string item;
vector<string> fruits;
while (getline(ss, item, ',')) {
    fruits.push_back(item);
}
// Converter strings para números
string num_str = "123";
int num = stoi(num_str);
double d = stod("3.14159");
string back_to_str = to_string(num);

Saída de Arquivo: Escrita de Arquivos

Escrever dados em arquivos de texto.

// Escrever no arquivo
ofstream outfile("output.txt");
if (outfile.is_open()) {
    outfile << "Hello, file!" << endl;
    outfile << "Number: " << 42 << endl;
    outfile.close();
}
// Anexar a arquivo existente
ofstream appendfile("log.txt", ios::app);
appendfile << "New log entry" << endl;
// Escrever vetor no arquivo
vector<int> numbers = {1, 2, 3, 4, 5};
ofstream numfile("numbers.txt");
for (int num : numbers) {
    numfile << num << " ";
}

Formatação de Fluxo

Controlar o formato e a precisão da saída.

#include <iomanip>
double pi = 3.14159265;
cout << fixed << setprecision(2) << pi << endl; // 3.14
cout << setw(10) << "Right" << endl;          // Alinhado à direita
cout << left << setw(10) << "Left" << endl;     // Alinhado à esquerda
cout << hex << 255 << endl;                    // Hexadecimal: ff

Tratamento de Erros

Blocos Try-Catch

Lidar com exceções que podem ocorrer durante a execução.

try {
    int result = 10 / 0; // Isso pode lançar uma exceção
    vector<int> vec(5);
    vec.at(10) = 100;    // Acesso fora dos limites

} catch (const exception& e) {
    cout << "Exceção capturada: " << e.what() << endl;
} catch (...) {
    cout << "Exceção desconhecida capturada!" << endl;
}
// Tipos de exceção específicos
try {
    string str = "abc";
    int num = stoi(str); // Lança invalid_argument
} catch (const invalid_argument& e) {
    cout << "Argumento inválido: " << e.what() << endl;
} catch (const out_of_range& e) {
    cout << "Fora do intervalo: " << e.what() << endl;
}

Lançamento de Exceções Personalizadas

Criar e lançar suas próprias exceções.

// Classe de exceção personalizada
class CustomException : public exception {
    string message;
public:
    CustomException(const string& msg) : message(msg) {}
    const char* what() const noexcept override {
        return message.c_str();
    }
};
// Função que lança exceção
void validateAge(int age) {
    if (age < 0 || age > 150) {
        throw CustomException("Faixa de idade inválida!");
    }
}
// Uso
try {
    validateAge(-5);
} catch (const CustomException& e) {
    cout << e.what() << endl;
}

Padrão RAII

Aquisição de Recurso É Inicialização para gerenciamento seguro de recursos.

// RAII com ponteiros inteligentes
{
    unique_ptr<int[]> arr = make_unique<int[]>(1000);
    // Array deletado automaticamente ao sair do escopo
}
// RAII com manipulação de arquivos
{
    ifstream file("data.txt");
    // Arquivo fechado automaticamente ao sair do escopo
    if (file.is_open()) {
        // Processar arquivo
    }
}
// Classe RAII personalizada
class FileHandler {
    FILE* file;
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
    }
    ~FileHandler() {
        if (file) fclose(file);
    }
    FILE* get() { return file; }
};

Asserções e Depuração (Debugging)

Depurar e validar suposições do programa.

#include <cassert>
#include <iostream>
void processArray(int* arr, int size) {
    assert(arr != nullptr);  // Asserção de depuração
    assert(size > 0);        // Valida a suposição

    // Processar array...
}
// Compilação condicional para saída de depuração
#ifdef DEBUG
    #define DBG_PRINT(x) cout << "DEBUG: " << x << endl
#else
    #define DBG_PRINT(x)
#endif
// Uso
DBG_PRINT("Iniciando função");

Processo de Compilação e Build

Compilação GCC/G++

Coleção de Compiladores GNU para C++.

# Compilação básica
g++ -o program main.cpp
# Com informações de depuração
g++ -g -o program main.cpp
# Níveis de otimização
g++ -O2 -o program main.cpp
# Múltiplos arquivos fonte
g++ -o program main.cpp utils.cpp math.cpp
# Incluir diretórios adicionais
g++ -I/usr/local/include -o program main.cpp
# Ligar bibliotecas
g++ -o program main.cpp -lm -lpthread

Padrões C++ Modernos

Compilar com versões de padrão C++ específicas.

# Padrão C++11
g++ -std=c++11 -o program main.cpp
# Padrão C++14
g++ -std=c++14 -o program main.cpp
# Padrão C++17 (recomendado)
g++ -std=c++17 -o program main.cpp
# Padrão C++20 (mais recente)
g++ -std=c++20 -o program main.cpp
# Habilitar todos os avisos
g++ -Wall -Wextra -std=c++17 -o program main.cpp

Noções Básicas de Makefile

Automatizar a compilação com a utilidade make.

# Makefile Simples
CXX = g++
CXXFLAGS = -std=c++17 -Wall -g
TARGET = program
SOURCES = main.cpp utils.cpp
$(TARGET): $(SOURCES)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCES)
clean:
rm -f $(TARGET)
.PHONY: clean

Melhores Práticas e Dicas

Convenções de Nomenclatura

A nomenclatura consistente torna o código mais legível.

// Variáveis e funções: snake_case ou camelCase
int student_count;
int studentCount;
void calculateAverage();
// Constantes: UPPER_CASE
const int MAX_BUFFER_SIZE = 1024;
const double PI = 3.14159;
// Classes: PascalCase
class StudentRecord {
    // Variáveis membro: prefixo com m_ ou sufixo _
    string m_name;
    int age_;

public:
    // Interface pública
    void setName(const string& name);
    string getName() const;
};

Segurança de Memória

Prevenir bugs comuns relacionados à memória.

// Usar ponteiros inteligentes em vez de ponteiros brutos
auto ptr = make_unique<int>(42);
auto shared = make_shared<vector<int>>(10);
// Inicializar variáveis
int count = 0;        // Bom
int count;            // Perigoso - não inicializado
// Loops baseados em intervalo são mais seguros
for (const auto& item : container) {
    // Processar item com segurança
}
// Verificar validade do ponteiro
if (ptr != nullptr) {
    // Seguro para desreferenciar
}

Dicas de Desempenho

Escrever código C++ eficiente.

// Passar objetos grandes por referência const
void processData(const vector<int>& data) {
    // Evitar copiar objetos grandes
}
// Usar pré-incremento para iteradores
for (auto it = vec.begin(); it != vec.end(); ++it) {
    // ++it é frequentemente mais rápido que it++
}
// Reservar capacidade do vetor quando o tamanho é conhecido
vector<int> numbers;
numbers.reserve(1000); // Evitar realocações
// Usar emplace em vez de push para objetos
vector<string> words;
words.emplace_back("Hello"); // Construir no local
words.push_back(string("World")); // Construir e depois copiar

Organização do Código

Estruturar o código para manutenção.

// Arquivo de cabeçalho (utils.h)
#ifndef UTILS_H
#define UTILS_H
class MathUtils {
public:
    static double calculateArea(double radius);
    static int fibonacci(int n);
};
#endif // UTILS_H
// Arquivo de implementação (utils.cpp)
#include "utils.h"
#include <cmath>
double MathUtils::calculateArea(double radius) {
    return M_PI * radius * radius;
}
// Usar funções membro const sempre que possível
double getRadius() const { return radius; }