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