A linguagem C++ surgiu da necessidade de lidar com sistemas cada vez mais complexos, onde a abordagem puramente estrutural do C apresentava limitações. Criada por Bjarne Stroustrup na década de 1980, a linguagem introduziu o suporte à Programação Orientada a Objetos (OOP), permitindo abstrações de alto nível sem sacrificar a performance de baixo nível herdada do C.
Evolução do C++
Desde suas origens como "C with Classes", a linguagem passou por diversas padronizações que moldaram o desanvolvimento de software moderno:
- C++98: Primeiro padrão oficial, introduzindo a Standard Template Libray (STL).
- C++11: Um marco histórico, trazendo
auto,nullptr, loops baseados em intervalo e semântica de movimento. - C++14/17: Refinamentos significativos em templates, inferência de tipos e constexpr.
Gerenciamento de Escopo com Namespaces
Para evitar conflitos de nomes em projetos de grande escala, o C++ utiliza namespaces. Eles permitem agrupar identificadores (variáveis, funções, classes) em escopos nomeados.
namespace Banco De Dados {
int conexoesAtivas = 0;
void Conectar() { /* ... */ }
}
namespace API {
int conexoesAtivas = 10; // Sem conflito com o namespace acima
}
int main() {
BancoDeDados::conexoesAtivas = 5;
return 0;
}
Podemos utilizar a diretiva using para simplificar o acesso, embora deva ser usada com cautela em arquivos de cabeçalho para evitar poluição de escopo.
Entrada e Saída de Dados
Diferente do printf e scanf do C, o C++ utiliza fluxos (streams) definidos na biblioteca <iostream>. A principal vantagem é a segurança de tipos, já que o compilador identifica automaticamente o formato do dado.
#include <iostream>
#include <string>
int main() {
int idade;
std::string nome;
std::cout << "Digite seu nome e idade: ";
std::cin >> nome >> idade;
std::cout << "Ola " << nome << ", voce tem " << idade << " anos." << std::endl;
return 0;
}
Parâmetros Padrão e Sobrecarga de Funções
O C++ permite que funções tenham argumentos com valores default. Se o chamador omitir o argumento, o valor pré-definido é utilizado.
void ConfigurarLog(int nivel = 1, bool salvarArquivo = true) {
// Lógica aqui
}
A sobrecarga de funções permite definir múltiplas funções com o mesmo nome, desde que suas listas de parâmetros (tipos ou quantidade) sejam distintas. O compilador utiliza a técnica de name mangling para diferenciar essas funções internamente.
int Processar(int a, int b) { return a + b; }
double Processar(double a, double b) { return a * b; }
O Uso de extern e static
O modificador extern declara um símbolo que está definido em outro arquivo, garantindo a ligação externa (external linkage). Já o static possui comportamentos distintso dependendo do contexto:
- Variáveis locais: Preservam seu valor entre chamadas de função.
- Variáveis globais/funções: Restringem a visibilidade ao arquivo atual (internal linkage).
- Membros de classe: Pertencem à classe em si, e não a instâncias específicas.
Constância e o Modificador mutable
O uso de const é fundamental para a robustez do código, impedindo modificações acidentais. No entanto, em classes, podemos marcar membros como mutable para permitir que sejam alterados mesmo dentro de métodos const.
class CacheSistema {
mutable int acessos = 0;
public:
int BuscarDados() const {
acessos++; // Permitido por ser mutable
return 42;
}
};
Referências vs. Ponteiros
Uma referência é um pseudônimo para uma variável existente. Ao contrário dos ponteiros, referências não podem ser nulas após a inicialização e não podem ser reatribuídas para referenciar outro objeto.
int original = 100;
int& ref = original; // ref agora aponta para o mesmo local de original
ref = 200; // original agora vale 200
Inferência de Tipo com auto
Introduzido no C++11, o auto permite que o compilador deduza o tipo da variável a partir do valor de inicialização. Isso é especialmente útil ao lidar com tipos complexos de iteradores da STL.
auto valor = 10.5; // double
auto it = lista.begin(); // O tipo exato é resolvido pelo compilador
Alocação Dinâmica e Placement New
O operador new aloca memória no heap e chama o construtor do objeto. Uma variante avançada é o Placement New, que permite construir um objeto em um endereço de memória já pré-alocado.
#include <new>
char buffer[sizeof(MinhaClasse)];
MinhaClasse* obj = new (buffer) MinhaClasse(123); // Construído no buffer
obj->~MinhaClasse(); // Destruição manual necessária neste caso
O Ponteiro Nulo Moderno: nullptr
Em C++11, o uso de NULL (frequentemente definido como 0) foi substituído por nullptr. O nullptr possui um tipo próprio (std::nullptr_t), o que resolve ambiguidades em sobrecargas de funções que aceitam inteiros ou ponteiros.
void Teste(int n);
void Teste(char* p);
int main() {
Teste(nullptr); // Chama Teste(char* p) sem ambiguidade
return 0;
}