Qual a diferença entre C e C ++?

21

Muitas pessoas disseram que o C ++ é uma linguagem completamente diferente da linguagem C, mas o próprio Bjarne disse que o C ++ é uma linguagem que se estende do C, portanto é daí que ++vem. Então, por que todo mundo continua dizendo que C e C ++ são linguagens completamente diferentes? De que maneira C é diferente de C ++, exceto os recursos estendidos em C ++?

Joshua Partogi
fonte
3
Devido a como eles são usados. Você certamente pode escrever C em C ++ ... mas não deveria.
Ed S.

Respostas:

18

Nos anos 80, quando o desenvolvimento do C ++ estava apenas começando, o C ++ era quase um superconjunto adequado de C. Foi assim que tudo começou.
No entanto, com o tempo, o C e o C ++ evoluíram e divergiram um do outro, embora a compatibilidade entre as linguagens seja sempre considerada importante.

Além disso, as diferenças técnicas entre C e C ++ tornaram os idiomas típicos nessas linguagens e o que é considerado "boa prática" diverge ainda mais.

Esse é o fator determinante por trás das pessoas que dizem coisas como "não existe linguagem como C / C ++" ou "C e C ++ são duas linguagens diferentes". Embora seja possível escrever programas aceitáveis ​​para um compilador C e C ++, o código geralmente é considerado nem um exemplo de bom código C nem um exemplo de bom código C ++.

Bart van Ingen Schenau
fonte
Não acho que o primeiro parágrafo esteja correto. Eu acredito que C sempre teve a conversão implícita, void *mas o C ++ nunca. (Não diminuiu o voto)
alternativa
5
@mathepic: defina "sempre". C tomou o tipo void * do C ++. No K&R C, o malloc estava retornando char * #
Nemanja Trifunovic
19

O próprio Stroustrup responde isso em seu FAQ :

C ++ é um descendente direto de C que retém quase todo C como um subconjunto. O C ++ fornece verificação de tipo mais forte que C e suporta diretamente uma variedade maior de estilos de programação que C. O C ++ é "um C melhor" no sentido de que suporta os estilos de programação feitos usando C com melhor verificação de tipo e mais suporte notacional (sem perda de eficiência). No mesmo sentido, o ANSI C é um C melhor que o K&R C. Além disso, o C ++ suporta abstração de dados, programação orientada a objetos e programação genérica.

É suporte para programação orientada a objetos e programação genérica que tornam o C ++ "completamente diferente" de C. Você pode quase escrever C puro e compilá-lo com um compilador C ++ (desde que você cuide da verificação de tipo mais rigorosa). Mas então você ainda está escrevendo C - não está escrevendo C ++.

Se você está escrevendo C ++, está usando os recursos orientados a objeto e de modelo e isso não é nada parecido com o que você veria em C.

Dean Harding
fonte
"isso não é nada parecido com o que você veria em C." Essa frase parece engraçada! "veja em C". C em C! Este é um tipo de poesia.
Galaxy
13

Simplificando, o que é considerado idiomático em C definitivamente não é idiomático em C ++.

C e C ++ são linguagens muito diferentes na prática, devido à maneira como as pessoas as usam. C visa ao minimalismo, onde C ++ é uma linguagem muito complexa, com muitos recursos.

Existem também algumas diferenças práticas: C pode ser facilmente chamado de praticamente qualquer linguagem e muitas vezes define a ABI de uma plataforma, enquanto C ++ é bastante difícil de usar em outras bibliotecas. A maioria das linguagens possui um FFI ou interface em C, mesmo as linguagens implementadas em C ++ (java, por exemplo).

David Cournapeau
fonte
4
Não é apenas que o código no estilo C não seja idiomático no C ++. A codificação no estilo C é realmente problemática em C ++, devido à falta de segurança de exceção.
dan04
4

Além do fato óbvio de que o C ++ suporta programação orientada a objetos, acho que você tem sua resposta aqui: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++

Esse artigo contém exemplos de código mostrando coisas que estão bem em C, mas não em C ++. Por exemplo:

int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */

A portabilidade de um programa C para C ++ geralmente é simples e consiste principalmente na correção de erros de compilação (adição de projeções, novas palavras-chave etc.).

Martin Wickman
fonte
4
Portar um programa como esse não fornece um programa C ++. Fornece C que pode ser compilado no compilador C ++. Isso não o torna C ++ (eu ainda chamaria o código resultante C (nem mesmo C com classes)).
Martin York
@ MartinMork Chame de C ruim, aponte que a semântica pode ser diferente e eu concordo. O resultado é obviamente C embora.
Deduplicator
2

O C ++ adiciona não apenas novos recursos, mas novos conceitos e novas expressões ao C. Embora o C ++ e o C estejam intimamente relacionados, o fato é que, para escrever efetivamente em uma linguagem, você deve pensar no estilo dessa linguagem. Mesmo o melhor código C não pode tirar proveito dos diferentes pontos fortes e idiomas do C ++ e, portanto, é mais provável do que não realmente um código C ++ ruim .

Jon Purdy
fonte
2

Os "recursos estendidos", você faz parecer que em C ++ eles adicionaram, macros variadas ou algo assim e é isso. Os "recursos estendidos" em C ++ são uma revisão completa da linguagem e substituem totalmente as melhores práticas de C, porque os novos recursos de C ++ são muito melhores que os recursos de C originais que os recursos de C originais são completa e totalmente redundantes na grande maioria dos casos . Sugerir que C ++ apenas estende C está sugerindo que um tanque de guerra moderno estenda um canivete para fins de guerra.

DeadMG
fonte
Você pode enumerar um exemplo de um dos recursos mais úteis que o C ++ possui e que o C ++ não possui?
Escuro Templar
2
@DarkTemplar: Que tal o gerenciamento de recursos de modo fácil com o RAII? Ou estruturas de dados genéricos legais usando modelos? Apenas para começar.
111111 DeadMG
1

A diferença é que em C você pensa proceduralmente e em C ++ você pensa de uma maneira orientada a objetos. As línguas são bastante semelhantes, mas a abordagem é muito diferente.

Formiga
fonte
C também não possui estruturas? Os arquivos C não são módulos separados (basicamente objetos)? Não tenho certeza qual é a diferença exata entre processual e orientada a objeto é ...
Escuro Templar
1
Escuro, você ilustrou meu argumento. Não é uma limitação da linguagem que permite escrever de maneira processual ou orientada a objetos; é um ethos ou uma maneira de pensar.
Ant
0

Enquanto C ++ pode ser um superconjunto de C em termos sintáticos - ou seja, qualquer construção do programa C pode ser compilada pelo compilador C ++.

No entanto, você quase nunca escreve programas em C ++ da maneira que faria com o programa em C. A lista pode ser interminável ou alguém deve apenas fazer mais pesquisas para colocá-la como relatórios exaustivos. No entanto, estou colocando alguns indicadores que fazem as principais diferenças.

O ponto da publicação atual é que o C ++ possui os seguintes recursos que um bom programador de C ++ deve usar como prática recomendada de programação, mesmo que seja possível compilar o equivalente em C.

Como isso deve ser feito em C ++ sobre C

  1. Classes e herança. Essas são as diferenças mais importantes que permitem orientação sistemática a objetos que tornam a expressão de programação muito poderosa. Eu acho - esse ponto não precisa de uma explicação melhor. Se você estiver em C ++ - quase sempre, é melhor usar classes.

  2. Privatização - Classes e até estruturas têm o que são membros privados. Isso torna possível o encapsulamento de uma classe. O equivalente em C é por meio da conversão do objeto como nulo * para o aplicativo, para que ele não tenha acesso a variáveis ​​internas. No entanto, em C ++, você pode ter elementos com as classes pública e privada.

  3. Passe por referência. O C ++ permite modificações com base na referência, para as quais são necessários indicadores de passagem. A passagem por referência mantém o código muito limpo e mais seguro contra os riscos do ponteiro. Você também passa o ponteiro no estilo C e isso funciona - mas se você estiver em C ++, será melhor desde que

  4. novo e excluir vs. malloc e gratuito. As instruções new () e delete () não apenas alocam e desalocam memória, mas também permitem que o código seja executado como parte do destruidor a ser chamado em uma cadeia. Se você estiver usando C ++ - é realmente ruim usar malloc e grátis.

  5. Tipos de E / S e sobrecarga do operador A sobrecarga do operador torna o código legível ou mais intuitivo se for bem executado. O mesmo para operadores << e >> io. A maneira C de fazer isso seria usar ponteiros de função - mas é confuso e apenas para programadores avançados.

  6. Usando "string". O caractere * de C funciona em qualquer lugar. Portanto, C e C ++ são praticamente os mesmos. No entanto, se você estiver em C ++ - é sempre muito melhor (e mais seguro) usar as classes String, o que evita os perigos das matrizes durante a execução, que são quase tudo.

Recursos dos quais ainda não seria fã no C ++ 1. Modelos - Embora eu não use modelos pesados ​​em muitos códigos - ele pode se tornar muito poderoso para bibliotecas. Não há quase nenhum equivalente em C. Mas em um dia normal - especialmente se você estiver faltando matematicamente.

  1. Ponteiros inteligentes - Sim, eles são muito inteligentes! E, como a maioria das coisas inteligentes - elas começam boas e ficam bagunçadas depois! Eu não gosto muito de usar

Coisas que eu gosto em C e sinto falta em C ++

  1. Algoritmos polimórficos usando ponteiros de função. Em C, quando você está executando algoritmos complexos - algumas vezes você pode usar um conjunto de ponteiros de função. Isso torna o polimorfismo verdadeiro de uma maneira poderosa. Quando você está em C ++, você PODE usar ponteiros de função - mas isso é ruim. Você deve usar apenas métodos - caso contrário, esteja preparado para ficar confuso. A única forma de polimorfismo nas classes C ++ é a sobrecarga de função e operador, mas isso é bastante limitante.

  2. Tópicos simples. Quando você cria threads, os pthreads são simples e gerenciáveis. Torna-se quando você precisa criar threads que deveriam ser "privados" para as classes (para que eles tenham acesso a membros privados). Há um tipo de estruturas de impulso - mas nada em C ++ básico.

Dipan.

Dipan Mehta
fonte
0

Certos recursos de idioma, mesmo que sejam adições, podem mudar o modo como o idioma praticamente precisa ser usado. Como um exemplo, considere este caso:

lock_mutex(&mutex);

// call some functions
...

unlock_mutex(&mutex);

Se o código acima envolvesse chamar funções implementadas em C ++, poderíamos enfrentar um mundo de problemas, pois qualquer uma dessas chamadas de função pode ser lançada e nunca desbloquearemos o mutex nesses caminhos excepcionais.

Os destruidores não estão mais no campo da conveniência para ajudar os programadores a evitar esquecer de liberar / liberar recursos nesse ponto. O RAII se torna um requisito prático, porque não é humanamente possível antecipar todas as linhas de código que podem apresentar exemplos não triviais (sem mencionar que essas linhas podem não ser lançadas agora, mas posteriormente com alterações). Veja outro exemplo:

void f(const Foo* f1)
{
    Foo f2;
    memcpy(&f2, f1, sizeof f2);
    ...
}

Esse código, embora geralmente inócuo em C, é como o fogo do inferno, causando estragos em C ++, porque as memcpytrincas nos bits e bytes desses objetos e ignoram coisas como construtores de cópias. Tais funções como memset, realloc, memcpy, etc, enquanto as ferramentas diárias entre desenvolvedores de C utilizado para olhar as coisas de uma forma bastante homogénea de bits e bytes na memória, não são harmoniosas com o mais complexo e mais rico sistema de tipo de C ++. O C ++ incentiva uma visão muito mais abstrata dos tipos definidos pelo usuário.

Portanto, esses tipos de coisas não permitem mais que o C ++, para quem quer usá-lo corretamente, olhe para ele como um mero "superconjunto" de C. Essas linguagens exigem uma mentalidade, disciplina e uma maneira de pensar muito diferentes para serem usadas com mais eficiência. .

Eu não estou no campo que vê o C ++ como totalmente melhor em todos os aspectos, e na verdade a maioria das minhas bibliotecas favoritas de terceiros são bibliotecas C por algum motivo. Não sei exatamente por que, mas as bibliotecas C tendem a ser de natureza mais minimalista (talvez porque a ausência de um sistema de tipo tão rico torne os desenvolvedores mais focados em fornecer a funcionalidade mínima necessária sem criar um conjunto grande e em camadas de abstrações), embora eu acabe colocando apenas wrappers C ++ em volta deles para simplificar e adaptar seu uso para meus propósitos, mas essa natureza minimalista é preferível a mim, mesmo ao fazer isso. Eu realmente amo o minimalismo como uma característica atraente de uma biblioteca para quem gasta um tempo extra para buscar essas qualidades, e talvez C tenda a incentivar isso,

Eu sou a favor do C ++ com muito mais frequência do que não, mas na verdade sou obrigado a usar APIs C com bastante frequência para obter a maior compatibilidade binária (e para FFIs), embora eu as implemente no C ++, apesar de usar C para os cabeçalhos. Mas, às vezes, quando você atinge um nível realmente baixo, como o nível de um alocador de memória ou uma estrutura de dados de nível muito baixo (e tenho certeza de que existem outros exemplos entre aqueles que fazem programação incorporada), às vezes pode ser útil capaz de assumir que os tipos e dados com os quais você está trabalhando estão ausentes de determinados recursos, como vtables, costructors e destructors, para que possamos tratá-los como bits e bytes para embaralhar, copiar, liberar, realocar. Para problemas de nível muito particularmente baixo, às vezes pode ser útil trabalhar com um sistema de tipo muito mais simples que C fornece,

Esclarecimento

Um comentário interessante aqui, eu gostaria de responder um pouco mais a fundo (acho que os comentários aqui são tão rigorosos quanto ao limite de caracteres):

memcpy(&f2, f1, sizeof f2); também é "o fogo do inferno destruindo o caos" em C se Foo tiver algum indicador de propriedade, ou é ainda pior, pois você também não possui as ferramentas para lidar com isso.

Esse é um ponto justo, mas tudo em que estou focando é predominantemente com foco no sistema de tipos do C ++ e também com relação ao RAII. Uma das razões pelas quais cópias de bytes de raios-x memcpyou qsorttipos de funções representam menos perigo prático em C é que a destruição de f1e f2acima é explícita (se eles precisam de destruição não trivial), enquanto que quando os destruidores entram em cena , eles se tornam implícitos e automatizados (geralmente com grande valor para os desenvolvedores). Isso não é nem mencionar o estado oculto, como vptrs e assim por diante, que essas funções atrapalhariam. Se f1possui ponteiros ef2raso as copia em algum contexto temporário, então não há problema se não tentarmos libertar explicitamente os proprietários de ponteiros pela segunda vez. Com o C ++, isso é algo que o compilador automaticamente desejará fazer.

E isso se torna ainda maior se tipicamente em C, " Se Foo possui ponteiros proprietários", porque a explicitação exigida com o gerenciamento de recursos geralmente tornará algo tipicamente mais difícil de ignorar, enquanto que em C ++, podemos fazer uma UDT não mais trivialmente construtível / destrutível, apenas fazendo com que ela armazene qualquer variável de membro que não seja trivialmente construtível / destrutível (de uma maneira que geralmente é muito útil, novamente, mas não se estivermos tentados a usar funções como memcpyou realloc).

Meu ponto principal não é tentar argumentar qualquer benefício dessa explicitação (eu diria que, se houver, eles quase sempre são oprimidos pelos contras da maior probabilidade de erro humano que o acompanha), mas apenas dizer que funções como memcpye memmovee qsorte memseterealloce assim por diante não têm lugar em uma linguagem com UDTs tão ricas em recursos quanto C ++. Embora eles existam independentemente, acho que não seria muito contestado dizer que a vasta e vasta maioria dos desenvolvedores de C ++ seria sensata em evitar funções como a praga, enquanto esses são tipos muito diários de funções em C, e eu ' argumentam que eles apresentam menos problemas em C pela simples razão de que seu sistema de tipos é muito mais básico e, talvez, "mais burro". Radiografar tipos C e tratá-los como bits e bytes é suscetível a erros. Fazer isso em C ++ é sem dúvida totalmente errado, porque essas funções estão lutando contra recursos muito fundamentais da linguagem e o que isso incentiva no sistema de tipos.

Esse é realmente o maior apelo para mim de C, no entanto, especificamente com o modo como ele se relaciona com a interoperabilidade de linguagem. Seria muito, muito mais difícil fazer algo como o FFI do C # entender os recursos completos do sistema e da linguagem do C ++, até construtores, destruidores, exceções, funções virtuais, sobrecarga de função / método, sobrecarga de operador, todos os vários tipos de herança, etc. Com C, é uma linguagem relativamente mais burra que se tornou bastante padrão nas APIs, de maneiras que muitos idiomas diferentes podem importar diretamente através de FFIs ou indiretamente através de algumas funções de exportação de API C na forma desejada (por exemplo: Java Native Interface ) E é aí que geralmente me resta não ter escolha a não ser usar C, pois essa interoperabilidade de idioma é um requisito prático no nosso caso (embora muitas vezes eu

Mas você sabe, sou um pragmático (ou pelo menos me esforço para ser). Se C era essa linguagem mais suja, horrível e propensa a erros, alguns dos meus entusiastas de C ++ alegavam ser (e eu me consideraria um entusiasta de C ++, exceto que, de alguma forma, isso não levou a um ódio de C de minha parte ; pelo contrário, teve o efeito oposto sobre mim de me fazer apreciar melhor as duas línguas em seus próprios aspectos e diferenças), então eu esperaria que isso aparecesse no mundo real na forma de algumas das coisas mais bugs e vazias e produtos e bibliotecas não confiáveis ​​sendo escritos em C. E não acho isso. Eu gosto do Linux, gosto do Apache, Lua, zlib, considero o OpenGL tolerável por seu longo legado contra requisitos de hardware tão variáveis, Gimp, libpng, Cairo, etc. Pelo menos, quaisquer que sejam os obstáculos que a linguagem represente, não parecem impasses, tanto quanto escrever algumas bibliotecas e produtos legais em mãos competentes, e é nisso que estou realmente interessado. Portanto, nunca fui do tipo que se interessa pelas pessoas mais guerras linguísticas, exceto para fazer um apelo pragmático e dizer: "Ei, há coisas legais por aí! Vamos aprender como elas foram criadas e talvez haja lições legais, não tão específicas da natureza idiomática da linguagem, que podemos trazer de volta para qualquer idioma que estamos usando ". :-D

Dragon Energy
fonte
2
memcpy(&f2, f1, sizeof f2);também é "o fogo do inferno destruindo o caos" em C se Footiver algum indicador de propriedade, ou é ainda pior, pois você também não possui as ferramentas para lidar com isso. Então, as pessoas que escrevem C não fazem coisas assim tanto
Caleth
@Caleth Parte do que simplifica que, mesmo com os ponteiros proprietários, é a liberação explícita dos ponteiros proprietários, para que efetivamente se transforme em uma cópia superficial nesses casos, com apenas um local ainda liberando a memória original, por exemplo (em alguns casos, dependendo da o design). Considerando que, com o envolvimento de destruidores, Fooagora as duas instâncias podem querer destruir a matriz dinâmica, ou vetor, ou algo nesse sentido. Muitas vezes, em alguns casos peculiares, acho útil escrever certas estruturas de dados para apoiar o fato de que a destruição é explícita e não implícita.
Dragon Energy
@ Caleth É reconhecidamente uma coisa de preguiça da minha parte, já que Foo, se houver algum indicador de propriedade, poderíamos fornecer a ele um copiador / movedor, um dtor, uma semântica de valores etc., e ter um código muito mais rico e seguro. Mas, ocasionalmente, me pego procurando C nos casos em que quero generalizar uma estrutura de dados ou alocador no nível homogêneo de bits e bytes, com certas suposições que posso fazer sobre os tipos que tendem a, em seus casos de uso restritos, ser simplificados um pouco por essas suposições que eu me sinto um pouco mais confiante em fazer C.
Dragon Energy
@Caleth No entanto, no meu caso, ocasionalmente, há alguma pedra angular da arquitetura em que não encontro utilidade em olhar mais do que bits e bytes na memória para organizar e acessar (e geralmente esses casos não envolvem nada além de PODs). Esses são os poucos casos peculiares em que eu ainda prefiro C. Se você imagina o alocador de memória, não há realmente mais nada para trabalhar além de bits e bytes, ponteiros nulos, coisas desse tipo, com todo o foco em alinhar e agrupar e distribuindo bits e bytes, e nesses casos muito peculiares, acho que C oferece menos obstáculos que C ++.
Dragon Energy
O outro caso em que às vezes alcanço C são os casos em que o código pode realmente se tornar mais generalizado e reutilizável, sendo menos abstrato e concentrando-se em primitivas, como API void filter_image(byte* pixels, int w, int h);um exemplo simples, ao contrário de gostar API void filter_image(ImageInterface& img);(que acoplaria nosso código a essa interface de imagem, estreitando sua aplicabilidade). Nesses casos, às vezes apenas implemento essas funções em C, pois há pouco a ganhar em C ++, e reduz a probabilidade de que esse código precise de alterações futuras.
Dragon Energy