Sobre C++

  1. Por que usar C++?
  2. Apelo: atualizações na linguagem
  3. C++ é C com std::cout?
  4. C++ é baixo-nível, assim como C?

Por que usar C++?

C++ é uma Linguagem de Propósito Geral (GPL -- General Purpose Language). Ou seja, é projetada para que o programador decida onde será utilizada. Por conta de algumas premissas descritas abaixo, há razões para compiladores de C++ geralmente prezarem por gerar código otimizado. Em resumo, C++ pode ser utilizado em lugares em que façam parte das preocupações do programador:

  1. Performance ótima, previsível e garantida, e isso é oferecido pela linguagem graças aos seus compiladores principais: g++, clang e CL (este último para o Microsoft Visual Studio Community/Ultimate);
  2. Abstração de operações, tipos, manipulação de memória, etc., sendo possível que operações e manipulações complexas possuam uma interfaces simples de se utilizar ou mesmo automatizar o gerenciamento de memória dinâmica sem a necessidade de um Garbage Collector;
  3. Compatibilidade de arquitetura: como o g++ é análogo a um irmão do gcc (compilador de C), que possui uma longa data de existência (e portanto mais pessoas se dedicaram a portar o compilador para suas plataformas), é possível compilar código C++ para arquiteturas diversas, principalmente microcontroladores que costumam ser preocupantemente difíceis de se ter um compilador de outra linguagem para eles. É possível, por exemplo, desenvolver para processadores diversos processadores ARM, mesmo antigos como ARM7TDMI (processador do console GameBoyAdvance) ou novos como os Cortex (como o dos celulares Android modernos e vários microcontroladores), ao mesmo tempo que é possível desenvolver para processadores voltados a Desktops e Notebooks, como os Intel/AMD x86 e x86_64;
  4. Recursos de programação moderna (a partir de C++11), mesmo para programação de software de base (como Sistemas Operacionais, Device Drivers, etc.): funções lambda, suporte a Threads, dentre outros;
  5. Necessidade de reimplementação de recursos básicos: é possível implementar qualquer recurso da biblioteca padrão da linguagem sem a necessidade de uma segunda linguagem, mantendo código eficiente, reutilizável e seguro. Assim, é possível ter uma implementação de algumas abstrações de uma forma para um sistema limitado em processamento ou memória, e de outra para um sistema em que restrições de processamento e memória não sejam um problema.
  6. Zero-cost Abstractions (Abstração de custo zero): é possível escrever camadas de abstração em C++ (seja para conectar duas interfaces não-compatíveis, ou oferecer uma API adequada a partir de outra, ou construir uma API completamente nova e independente) sem que isso imponha custo adicional. O compilador deve ser capaz de otimizar e eliminar tais camadas sempre que possível (por exemplo, uma chamada para função que retorne um valor fixo é substituída pelo próprio valor retornado), dentre outras otimizações relacionadas a abstração (seja de tipos, operações ou arquitetura);
  7. Semântica de "move": a linguagem oferece a possibilidade de evitar cópias desnecessárias (ou que não devem existir) em contextos diversos.

Apelo: atualizações na linguagem

Um pequeno apelo a ser feito é sempre procurar informações atualizadas a respeito da linguagem. O motivo é que, em 2011, um novo padrão da linguagem foi aceito e incorporado a ela, chamado de C++11. Esse novo padrão define novos recursos que melhoraram a qualidade da linguagem de deixando-a desde mais segura e robusta a uma linguagem mais moderna, simplificando bastante a forma de se programar nela. Logo, a forma de se programar em C++ pré-11 pode ser dita como obsoleta. Inclusive, o advento de C++14 e C++17 (lançados em 2014 e 2017, respectivamente), já fazem de C++11 um padrão obsoleto em algumas partes.

Não há por que perder tempo aprendendo C++ antigo: boa parte das _toolchains_ já lidam com C++ moderno, que além de possuir uma biblioteca padrão mais ampla e robusta, é repleto de recursos novos que inutilizam boa parte do trabalho braçal das antigas versões de C++.

Como exemplo, o código abaixo exemplifica a criação e iteração por elementos de um Map em C++ antigo (pré-C++11):

std::map<std::string, int> data = std::map<std::string, int>();
data.insert(std::pair<std::string, int>{"x", 4});
data.insert(std::pair<std::string, int>{"y", 4});
data.insert(std::pair<std::string, int>{"z", 12});

for (std::map<std::string, int>::iterator it = data.begin();
     it != data.end();
     ++it) {
    std::cout << it->first << ": " << it->second << "\n";
}

O mesmo código, em C++11:

auto data = std::map<std::string, int>{
    {"x", 4},
    {"y", 4},
    {"z", 12},
};

for (auto elm: data) {
    std::cout << elm.first << ": " << elm.second << "\n";
}

E por fim, em C++17:

auto data = std::map<std::string, int>{
    {"x", 4},
    {"y", 4},
    {"z", 12},
};

for (auto [key, value]: data) {
    std::cout << key << ": " << value << "\n";
}

Comparando com um código equivalente em Python:

data = {
    'x': 4,
    'y': 4,
    'z': 12,
}

for key, value in data:
    print(f'{key}: {value}')

Isso deve deixar claro por que buscar trabalhar com C++ moderno.

C++ é C com std::cout?

Não, C++ não é C com std::cout. Como C++ surgiu baseada em C (inclusive, suas primeiras implementações geravam código C) e sempre prometeu uma certa retrocompatibilidade com C, é comum ver construções em ambas as linguagens que sejam parecidas, ou mesmo código em C que compile em C++. Por consequência, se criam crenças de que entender de C implica em entender de C++ (e vice-versa), além de ser comum programadores escreverem código C no meio de código C++. Mas, serve de regra:

_"C em código C++ é C++ ruim."_ C++ possui suas próprias mecânicas para lidar com segurança, memória e algoritmos. Além disso, alguns comandos possuem significado diferente em C e em C++.

Por exemplo:

  • Em C é comum e necessário trabalhar com ponteiros explicitamente. Em C++ isso é, para praticamente todo caso, um erro (por conta de diversos problemas de segurança relacionados a ponteiros, undefined-behaviours não muito óbvios ao programador, e por C++ oferecer recursos melhores para gerenciá-los).
  • O mesmo se aplica ao uso da diretiva #define: para se escrever código genérico em C, #define é essencial. Porém C++ possui seu próprio mecanismo de código genérico: Templates, que reduzem a possibilidade de erros (veja a Recomendação de Leitura #1) e aproveitam melhor o sistema de tipos da linguagem.

C++ é baixo-nível, assim como C?

Não, pelos mesmos motivos pelo qual C também não é. Mas, é claro, várias construções da linguagem são capazes de dar essa ilusão ao programador, por exemplo: gerenciamento manual de memória, suporte nativo da linguagem para referência explícita de locais na memória, tipos primitivos dependentes da arquitetura, o fato de que compiladores de C e C++ costumam gerar código nativo, dentre outros.

Documentação Recomendada

É recomendado procurar a documentação no site cpp-reference, que costuma estar atualizado, conter boas explicações, indicações claras das versões de C++, exemplos de uso, e conformidade com o que se espera de código moderno da linguagem.

Outra documentação frequentemente encontrada é o C-plus-plus, porém ela costuma dar exemplos pouco idiomáticos e muitas vezes misturando código C em exemplos de C++.

Recomendações de Leitura

  1. #define's são seguros?