Histórico / Cenário
Comecei a escrever um aplicativo CLI puramente em C (meu primeiro programa C ou C ++ adequado que não era "Hello World" ou uma variação dele). No meio do caminho, eu estava trabalhando com "strings" de entrada do usuário (matrizes de caracteres) e descobri o objeto streamer de strings C ++. Vi que podia salvar código usando esses, então usei-os através do aplicativo. Isso significa que alterei a extensão do arquivo para .cpp e agora compile o aplicativo em g++
vez de gcc
. Portanto, com base nisso, eu diria que o aplicativo agora é tecnicamente um aplicativo C ++ (embora mais de 90% do código esteja escrito no que eu chamaria de C, pois há muito cruzamento entre os dois idiomas, dada minha experiência limitada em os dois). É um único arquivo .cpp com cerca de 900 linhas.
Fatores Importantes
Quero que o programa seja gratuito (como em dinheiro), livremente distribuível e utilizável para todos. Minha preocupação é que alguém olhe o código e pense algo com o efeito de:
Oh, olhe a codificação, é horrível, este programa não pode me ajudar
Quando potencialmente poderia! Outra questão é o código ser eficiente (é um programa para testar a conectividade Ethernet). Não deve haver partes do código que sejam tão ineficientes que possam prejudicar gravemente o desempenho do aplicativo ou sua saída. No entanto, acho que essa é uma pergunta para o Stack Overflow ao pedir ajuda com funções, métodos, chamadas de objetos, etc.
Minha pergunta
Tendo (na minha opinião) misturado C e C ++ onde talvez eu não devesse. Devo procurar reescrever tudo em C ++ (com isso, quero dizer implementar mais objetos e métodos C ++, onde talvez eu tenha codificado algo em um estilo C que possa ser condensado usando técnicas mais recentes de C ++) ou remover o uso de objetos de streamer de string e trazer tudo "de volta" ao código C? Existe uma abordagem correta aqui? Estou perdido e preciso de algumas orientações sobre como manter esta aplicação "Bom" aos olhos das massas, para que elas a usem e se beneficiem.
O Código - Atualização
Aqui está um link para o código. São cerca de 40% dos comentários, comento quase todas as linhas até me sentir mais fluente. Na cópia a que vinculei, removi praticamente todos os comentários. Espero que isso não dificulte a leitura. Espero, porém, que ninguém precise compreendê-lo completamente. Se eu cometi falhas fatais de design, espero que sejam identificáveis facilmente. Devo também mencionar, estou escrevendo alguns desktops e laptops Ubuntu. Não pretendo portar o código para outros sistemas operacionais.
LICENSE
arquivo. Você pode obter um feedback interessante.Respostas:
Vamos começar do começo: códigos C e C ++ mistos são bastante comuns. Então você está em um grande clube para começar. Temos enormes bases de código C na natureza. Mas, por razões óbvias, muitos programadores se recusam a escrever pelo menos coisas novas em C, tendo acesso ao C ++ no mesmo compilador, novos módulos começam a ser escritos dessa maneira - a princípio, deixando apenas as partes existentes.
Eventualmente, alguns arquivos existentes são recompilados como C ++ e algumas pontes podem ser excluídas ... Mas isso pode levar muito tempo.
Você está um pouco à frente, seu sistema completo agora é C ++, apenas a maior parte está escrita em "estilo C". E você vê um mix de estilos um problema, o que você não deveria: C ++ é uma linguagem de paradigmas múltiplos que suporta muitos estilos e permite que eles coexistam para sempre. Na verdade, essa é a força principal, que você não é forçado a um estilo único. Um que seria abaixo do ideal aqui e ali, com alguma sorte, não em todo lugar.
Retrabalhar a base de código é uma boa ideia, se estiver quebrada. Ou se estiver no caminho do desenvolvimento. Mas se funcionar (no sentido original da palavra), siga o princípio mais básico de engenharia: se não estiver quebrado, não conserte. Deixe as peças frias em paz, coloque seu esforço onde é importante. Nas peças que são ruins, perigosas - ou em novos recursos, basta refatorar as peças para torná-las uma cama.
Se você procura informações gerais, aqui está o que vale a pena despejar de uma base de código C:
Quando você termina, aproxima-se do ponto em que a base de código pode ser razoavelmente segura para exceções, para que você possa descartar o código de retorno usando exceções e rara tentativa / captura apenas em funções de alto nível.
Além disso, basta escrever um novo código em algum C ++ íntegro e, se nascerem algumas classes que substituem o código existente, escolha-as.
Eu não mencionei coisas relacionadas à sintaxe, obviamente use refs em vez de ponteiros em todo o novo código, mas substituir peças C antigas apenas para essa alteração não é um bom valor. Transmissões que você deve resolver, eliminar tudo o que puder e usar variantes do C ++ nas funções de wrapper para o restante. E muito importante, adicione const sempre que aplicável. Estes intercalam com as balas anteriores. E consolide suas macros e substitua o que você pode transformar em enum, função embutida ou modelo.
Sugiro ler os Padrões de Codificação C ++ de Sutter / Alexandrescu, se ainda não tiverem sido executados, e segui-los de perto.
fonte
Resumindo: você não está indo para o inferno, nada de ruim vai acontecer, mas também não vai ganhar nenhuma competição de beleza.
Embora seja perfeitamente possível usar o C ++ como um "C melhor", você está jogando fora muitos dos benefícios do C ++, mas como não está se restringindo à baunilha C, não está obtendo nenhum dos benefícios do C (simplicidade, portabilidade , transparência, interoperabilidade). Em outras palavras: C ++ sacrifica algumas das qualidades de C para obter outras - por exemplo, a transparência de C, onde você sempre pode ver claramente onde e quando ocorrem as alocações de memória, é negociado pelas abstrações mais poderosas de C ++.
Como seu código parece funcionar bem agora, provavelmente não é uma boa idéia reescrevê-lo apenas para mantê-lo: mantenha-o como está agora e altere-o para C ++ mais idiomático conforme você for, um pedaço de cada vez, sempre que você trabalha em qualquer parte. E lembre-se das lições aprendidas assim para o seu próximo projeto, principalmente este: C e C ++ não são a mesma linguagem, e é melhor fazer uma escolha antecipada do que decidir mudar para C ++ na metade do seu projeto .
fonte
C ++ é uma linguagem muito complexa, no sentido de que possui muitos recursos. É também uma linguagem que suporta múltiplos paradigmas, o que significa que permite escrever código inteiramente procedural sem usar nenhum objeto.
Portanto, como o C ++ é tão complexo, você geralmente vê as pessoas usarem um subconjunto limitado de seus recursos em seus códigos. Os iniciantes geralmente usam apenas a E / S do fluxo, os objetos de sequência e new / delete em vez de malloc / free, e talvez referências em vez de ponteiros. À medida que aprende sobre os recursos orientados a objetos, você pode começar a escrever em um estilo chamado "C com classes". Eventualmente, à medida que você aprende mais sobre C ++, você começa a usar modelos, RAII , STL , ponteiros inteligentes etc.
O que estou tentando enfatizar é que o aprendizado de C ++ é um processo que leva tempo. Sim, agora seu código provavelmente parece ter sido escrito por um programador C tentando escrever C ++. E já que você está apenas aprendendo, tudo bem. No entanto, isso me faz estremecer quando vejo esse tipo de coisa no código de produção escrito por programadores experientes que deveriam conhecer melhor.
Lembre-se, um bom programador Fortran pode escrever um bom código Fortran em qualquer idioma. :)
fonte
Parece que você está recapitulando exatamente o caminho que muitos programadores antigos de C seguiram ao acessar o C ++. Você está usando como um "C melhor". Vinte anos atrás, isso fazia algum sentido, mas neste momento há tantas construções mais poderosas em C ++ (como a Standard Template Library) que raramente faz sentido agora. No mínimo, você provavelmente deve fazer o seguinte para evitar aneurismas de programadores em C ++ ao analisar seu código:
printf
família.new
vez demalloc
.std::string
vez dechar*
sempre que possívelSe o seu programa for curto (e ~ 900 linhas parecerem curtas), pessoalmente não acho que seja necessário ou útil a tentativa de criar um conjunto robusto de classes.
fonte
A resposta aceita menciona apenas os profissionais da conversão de C em C ++ idiomático, como se o código C ++ fosse, em algum sentido absoluto, melhor que o código C. Concordo com outras respostas que provavelmente não é necessário fazer alterações drásticas no código misto se não estiver quebrado.
Se a mistura de C e C ++ é sustentável depende de como a mistura é feita. Erros sutis podem ser introduzidos, por exemplo, quando exceções são geradas no código C (o que não é seguro), causando vazamentos de memória ou corrupção de dados. Uma maneira mais segura e bastante comum é agrupar bibliotecas C ou interfaces em classes ou usá-las em algum outro tipo de isolamento em um projeto C ++.
Antes de decidir reescrever todo o seu projeto em C ++ idiomático (ou C), você deve estar ciente de que muitas das alterações apresentadas em outras respostas podem tornar seu programa mais lento ou causar outros efeitos indesejados. Por exemplo:
Para concluir, a conversão de códigos C e C ++ mistos para C ++ idiomático deve ser vista como uma troca que tem muito mais do que apenas tempo de implementação e ampliando sua caixa de ferramentas com recursos aparentemente convenientes. Portanto, é muito difícil dar uma resposta para o caso geral que não seja "depende".
fonte
É uma má forma misturar C e C ++. Eles são idiomas distintos e realmente devem ser tratados como tal. Escolha a que mais lhe agrada e tente escrever código idiomático nesse idioma.
Se C ++ estiver poupando muito código, fique com C ++ e reescreva as partes C. Duvido que uma diferença de desempenho seja perceptível, se é com isso que você está preocupado.
fonte
Eu ando de um lado para o outro entre C e C ++ o tempo todo e sou o tipo ímpar que prefere trabalhar dessa maneira. Prefiro C ++ para coisas de nível superior, enquanto uso o C como um instrumento contundente que me permite radiografar tipos de dados e tratá-los como bits e bytes com, digamos,
memcpy
que acho úteis para implementar estruturas de dados de baixo nível e alocadores de memória . Se você estiver trabalhando no nível de bits e bytes brutos, o sistema de tipos realmente rico do C ++ não ajuda em nada, e geralmente acho mais fácil escrever esse código em C, onde posso assumir com segurança que posso trate qualquer tipo de dados C como apenas bits e bytes.A principal coisa a ter em mente é onde essas duas línguas não se combinam bem.
1. A função AC nunca deve chamar uma função C ++ que possa
throw
. Dado quantos lugares seu tipo diário de código C ++ pode implicitamente executar em uma exceção, isso geralmente significa que suas funções C não devem chamar as funções C ++ em geral. Caso contrário, seu código C não poderá liberar os recursos que ele aloca durante o desenrolamento da pilha, pois não há uma maneira prática de capturar a exceção C ++. Se você acaba exigindo que seu código C chame uma função C ++ como retorno de chamada, por exemplo, o lado C ++ deve garantir que qualquer exceção encontrada seja capturada antes de retornar ao código C.2. Se você escrever um código C que trata os tipos de dados apenas como bits e bytes brutos, fazendo um bulldozing no sistema de tipos (esse é realmente o meu principal motivo para usar C em primeiro lugar), nunca desejará usar esse código nos dados do C ++ tipos que podem ter construtores e destruidores de cópia, ponteiros virtuais e coisas desse tipo que precisam ser respeitadas. Portanto, geralmente deve ser o código C usando o código C desse tipo, não o código C ++ usando o código C desse tipo.
Se você deseja que seu código C ++ use esse código C, normalmente você deseja usá-lo como o detalhe de implementação de uma classe que garante que os dados que ele está armazenando na estrutura de dados C genérica são um tipo de dados que é fácil de construir. e destruir. Felizmente, existem características de tipo como essas que você pode usar para verificar isso em uma declaração estática, certificando-se de que os tipos que você está armazenando na estrutura de dados C genérica tenham destruidores e construtores triviais e permanecerão assim.
Na minha opinião, você não precisa se preocupar se seguir as regras acima e garantir que seu código seja bem testado nos testes que você escreveu. A parte que pode valer a pena melhorar é o código que você escreveu em C ++ até agora, mas isso não significa que você precise portar o código estritamente baseado em C para C ++.
Com o código que você escreveu em C ++ e compila como código em C ++, é preciso ter cuidado. Usar codificação do tipo C em C ++ está causando problemas. Por exemplo, se você alocar e liberar recursos manualmente, é provável que seu código não seja seguro para exceções, pois ele pode encontrar uma exceção no momento em que você sai implicitamente da função antes de poder utilizá
free
-lo. No C ++, o RAII e coisas como ponteiros inteligentes não são uma conveniência simples, pois podem aparecer se você apenas olhar para os caminhos de execução normais sem considerar caminhos excepcionais. Geralmente, são uma necessidade fundamental de escrever um código correto e seguro para exceções de maneira simples e eficaz, que não começará a vazar por todo o lugar ao encontrar uma exceção.fonte