Vejo algumas respostas válidas aqui, mas vou detalhar um pouco mais.
Vá para o resumo abaixo para obter a resposta para sua pergunta principal, se você não quiser passar por toda essa parede de texto.
Abstração
Então, neste caso, pelo que estou pagando?
Você está pagando pela abstração . Ser capaz de escrever códigos mais simples e mais amigáveis ao homem tem um custo. No C ++, que é uma linguagem orientada a objetos, quase tudo é um objeto. Quando você usa qualquer objeto, três coisas principais sempre acontecem sob o capô:
- Criação de objeto, basicamente alocação de memória para o próprio objeto e seus dados.
- Inicialização de objeto (geralmente através de algum
init()
método). Normalmente, a alocação de memória acontece sob o capô como a primeira coisa nesta etapa.
- Destruição de objetos (nem sempre).
Você não o vê no código, mas toda vez que você usa um objeto, todas as três coisas acima precisam acontecer de alguma forma. Se você fizesse tudo manualmente, o código seria obviamente muito mais longo.
Agora, a abstração pode ser feita de maneira eficiente, sem acrescentar sobrecarga: o inlining de método e outras técnicas podem ser usadas pelos compiladores e programadores para remover as sobrecargas da abstração, mas esse não é o seu caso.
O que realmente está acontecendo em C ++?
Aqui está, dividido:
- A
std::ios_base
classe é inicializada, que é a classe base para tudo relacionado a E / S.
- O
std::cout
objeto é inicializado.
- Sua string é carregada e passada para
std::__ostream_insert
, que (como você já descobriu pelo nome) é um método std::cout
(basicamente o <<
operador) que adiciona uma string ao fluxo.
cout::endl
também é passado para std::__ostream_insert
.
__std_dso_handle
é passado para __cxa_atexit
, que é uma função global responsável pela "limpeza" antes de sair do programa. __std_dso_handle
ele próprio é chamado por essa função para desalocar e destruir os objetos globais restantes.
Então, usando C == não está pagando por nada?
No código C, muito poucas etapas estão acontecendo:
- Sua string é carregada e passada
puts
através do edi
registro.
puts
é chamado.
Nenhum objeto em qualquer lugar, portanto, não há necessidade de inicializar / destruir nada.
Este, porém, não significa que você não está "pagando" para qualquer coisa em C . Você ainda está pagando pela abstração, e também pela inicialização da biblioteca padrão C e da resolução dinâmica. A printf
função (ou, na verdade puts
, que é otimizada pelo compilador, pois você não precisa de nenhuma sequência de formatação) ainda acontece sob o capô.
Se você escrevesse este programa em assembly puro, seria algo como isto:
jmp start
msg db "Hello world\n"
start:
mov rdi, 1
mov rsi, offset msg
mov rdx, 11
mov rax, 1 ; write
syscall
xor rdi, rdi
mov rax, 60 ; exit
syscall
O que basicamente resulta apenas em invocar o write
syscall seguido pelo exit
syscall. Agora, esse seria o mínimo necessário para realizar a mesma coisa.
Resumir
C é bem mais simples , e apenas o mínimo necessário, deixando total controle ao usuário, que é capaz de otimizar e personalizar totalmente basicamente o que quiser. Você diz ao processador para carregar uma string em um registro e, em seguida, chama uma função de biblioteca para usar essa string. C ++, por outro lado, é muito mais complexo e abstrato . Isso tem uma enorme vantagem ao escrever códigos complicados e permite a criação de códigos mais fáceis e mais amigáveis ao ser humano, mas obviamente tem um custo. Sempre haverá uma desvantagem no desempenho do C ++ se comparado ao C em casos como esse, pois o C ++ oferece mais do que o necessário para realizar essas tarefas básicas e, portanto, acrescenta mais sobrecarga .
Respondendo à sua pergunta principal :
Estou pagando pelo que não estou comendo?
Nesse caso específico, sim . Você não está tirando proveito de nada que o C ++ tenha a oferecer mais que o C ++, mas isso é apenas porque não há nada nesse trecho de código simples com o qual o C ++ possa ajudá-lo: é tão simples que você realmente não precisa do C ++.
Ah, e só mais uma coisa!
As vantagens do C ++ podem não parecer óbvias à primeira vista, pois você escreveu um programa muito simples e pequeno, mas observe um exemplo um pouco mais complexo e veja a diferença (os dois programas fazem exatamente a mesma coisa):
C :
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main(void) {
int i, n, *arr;
printf("How many integers do you want to input? ");
scanf("%d", &n);
arr = malloc(sizeof(int) * n);
for (i = 0; i < n; i++) {
printf("Index %d: ", i);
scanf("%d", &arr[i]);
}
qsort(arr, n, sizeof(int), cmp)
puts("Here are your numbers, ordered:");
for (i = 0; i < n; i++)
printf("%d\n", arr[i]);
free(arr);
return 0;
}
C ++ :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void) {
int n;
cout << "How many integers do you want to input? ";
cin >> n;
vector<int> vec(n);
for (int i = 0; i < vec.size(); i++) {
cout << "Index " << i << ": ";
cin >> vec[i];
}
sort(vec.begin(), vec.end());
cout << "Here are your numbers:" << endl;
for (int item : vec)
cout << item << endl;
return 0;
}
Espero que você possa ver claramente o que quero dizer aqui. Observe também como em C você precisa gerenciar a memória em um nível mais baixo usando malloc
e free
como precisa ser mais cuidadoso com a indexação e os tamanhos e como precisa ser muito específico ao receber entradas e impressões.