Guia de boa práticas em C++
Introdução
As informações apresentadas neste guia foram obtidas de alguns materiais (livros e blogs), citados nas referências, e de conhecimento prático pessoal.
Achei importante criar este material em PT_BR pois a maioria dos materiais encontrados estão escritos na língua do Tio Sam, o que dificulta um pouco o entendimento, principalmente para os iniciantes. Além disso, tentei resumir de forma bem prática e direta alguns conceitos básicos de forma que este material possa ser utilizado como um guia de consulta rápida.
Caso você não concorde com algo ou tenha alguma informação a acrescentar, sinta-se à vontade para comentar.
Estilo de Código
Todo projeto possui seu estilo de código, alguns com algumas práticas mais avançadas e outros praticamente sem nenhum padrão. Porém, o estilo de um código tem grande impacto em sua respectiva legibilidade. Sendo assim, é importante investir algumas horas do seu tempo para estudar um pouco sobre isso, além de realizar revisões de código sempre que possível, garantindo um código mais fácil de manter e evoluir.
Nomes de Variáveis
Variáveis devem sempre começar com letra minúscula, por exemplo:
string myWeirdVariable; // ou string my_weird_variable;
string MyWeird_Variable2; // ou string My_weirdVariable_3;
Utilize um padrão já conhecido para a declaração das variáveis, como por exemplo:
Eu pessoalmente prefiro utilizar o padrão CamelCase e vejo muita gente utilizando ele também. Mas isso não significa que você deva necessariamente utilizá-lo. O mais importante é manter a consistência na declaração das variáveis.
Nomes de Constantes
Constantes devem ser declaradas sempre em letras maiúsculas (caixa alta):
const double PI = 3.14159;
const double pi = 3.14159;
Nomes de Funções
Nomes de funções devem começar com a primeira letra minúscula, assim como as variáveis:
void myFunction();
void MyFunction();
void My_Function();
Nomes de Classes
Nomes de classes devem começar com a primeira letra maiúscula e seguir o padrão CamelCase (preferencialmente):
class LinkedList
class linkedList
Comentários
Utilize //
para blocos de comentários (comentários de múltiplas linhas) dentro de funções, por exemplo:
bool equal( int value1, int value2 ) { // Compara dois valores e retorna // verdadeiro se os valores são iguais if( value1 == value2 ) { return true; } return false; }
Caso seja necessário comentar um bloco de código para debugar ou por algum outro motivo, você não terá problemas, por exemplo:
bool equal( int value1, int value2 ) { /* // Compara dois valores e retorna // verdadeiro se os valores são iguais if( value1 == value2 ) { return true; } */ return false; }
Caso contrário, não seria possível comentar o bloco de código inteiro, por exemplo:
bool equal( int value1, int value2 ) { /* /* * Compara dois valores e retorna * verdadeiro se os valores são iguais */ if( value1 == value2 ) { return true; } */ return false; }
Além disso, na minha opinião, quando é utilizado //
para comentários de múltiplas linhas o código parece ser mais legível do que quando se utiliza /* */
.
Indentação
O mais comum é a indentação ou recuo de código utilizando 4 espaços, 2 espaços ou 1 tab. Isso pode mudar de projeto para projeto ou mesmo de acordo com a linguagem de programação. Eu pessoalmente costumo utilizar 4 espaços e acredito que este seja o padrão mais utilizado pelos desenvolvedores. É possível configurar a IDE ou o editor para utilizar por padrão o indentação desejada.
Não utilize números mágicos
Não utilize números ‘mágicos’, por exemplo:
double calc( double value ) { return value * 3.14159; }
Nestes casos opte por definir uma constante, por exemplo:
const double PI = 3.14159; double calc( double value ) { return value * PI; }
Mas utilize, SIM, números, quando isso fizer sentido, por exemplo:
double calc( double value ) { return value * 2; }
#define TWO 2 double calc( double value ) { return value * TWO; }
Inclua guards
Arquivos de cabeçalho (header files) devem utilizar guards para evitar problemas com a inclusão do mesmo arquivo múltiplas vezes e previnir conflitos com cabeçalhos de outros projetos:
#ifndef MYCLASS_H #define MYCLASS_H class MyClass { public: void myFunc(); }; #endif
class MyClass { public: void myFunc(); };
Sempre utilize chaves
Sempre utilize chaves mesmo quando existe apenas uma linha de código dentro de um bloco. A não utilização de chaves pode causar erros semânticos no código, por exemplo:
int sum = 0; for (int i = 0; i < 15; ++i) { ++sum; std::cout << i << std::endl; }
for (int i = 0; i < 15; ++i) std::cout << i << std::endl;
int sum = 0; for (int i = 0; i < 15; ++i) ++sum; std::cout << i << std::endl;
Mantenha as linhas com um comprimento razoável
Mantenha as linhas com um comprimento razoável. Caso a linha seja muito extensa, tenha muitos caracteres, vale a pena quebrá-la em múltiplas linhas, por exemplo:
if( (x == 1 && y == 2 && myFunction() == true) || (x == 0 && y == 0 && myFunction() == false) ) { }
if( (x == 1 && y == 2 && myFunction() == true) || (x == 0 && y == 0 && myFunction() == false) ) { }
Utilize aspas duplas para incluir arquivos locais
Utilize aspas duplas (""
) para incluir arquivos locais.
#include <string> #include <MyHeader.hpp>
#include <string> #include "MyHeader.hpp"
Utilize constantes sempre que possível
Utilize const
sempre que possível. const
avisa ao compilador que a variável é imutável. Isto auxilia o compilador a otimizar o código e ajuda o programador a saber se uma função tem “efeitos colaterais”. Ainda, a utilização de const &
previne o compilador de copiar dados desnecessariamente.
class MyClass { public: void do_something(int i); void do_something(std::string str); };
class MyClass { public: void do_something(const int i); void do_something(const std::string &str); };
Passe ou retorne tipos simples por valor
Não passe ou retorne tipos simples por referência, mas sim por valor:
class MyClass { public: explicit MyClass(const int& t_int_value) : m_int_value(t_int_value) { } const int& get_int_value() const { return m_int_value; } private: int m_int_value; }
Se o valor não será alterado é possível utilizar const
.
class MyClass { public: explicit MyClass(const int t_int_value) : m_int_value(t_int_value) { } int get_int_value() const { return m_int_value; } private: int m_int_value; }
Utilize a passagem de parâmetro por referência para objetos, vetores, etc.
Utilize double em vez de float
A utilização de float
irá reduzir a precisão. Porém, em operações com vetores float
pode ser mais rápido que double
se você puder sacrificar a precisão.
Contudo, double
é a opção padrão recomendada já que este é o tipo padrão para valores de ponto flutuante em C++.
Dicas
Nesta seção você irá encontrar algumas dicas importantes que podem ser úteis durante o desenvolvimento.
Lembre-se de deletar os ponteiros
Lembre-se de sempre deletar os ponteiros para liberar a memória alocada. Além de deletar o ponteiro, eu costumo definir ele como NULL
para evitar comportamento indefinido (isso faz mais sentido quando o ponteiro está no escopo da classe e não da função).
double myFunction(double value1, double value2) { Calculator *calc = new Calculator(); double result = calc->sum(value1, value2); delete calc; calc = NULL; return result; }
double myFunction(double value1, double value2) { Calculator *calc = new Calculator(); return calc->sum(value1, value2); }
Contudo, opte por utilizar ponteiros inteligentes (próximo tópico) sempre que possível.
Utilize ponteiros inteligentes
Sempre que possível utilize ponteiros inteligentes (smart pointers) ao invés de utilizar os ponteiros tradicionais (raw pointers). O uso de ponteiros inteligentes pode evitar diversos problemas, dentre eles o vazamento de memória (memory leak).
void ponteiroInteligente() { // Declare um ponteiro inteligente na pilha e passe o ponteiro tradicional (ponteiro bruto) unique_ptr<Song> pSong(new Song(L"Nothing on You", L"Bruno Mars")); // Utilize pSong... // Exemplo: pSong->duration(); // pSong é deletado automaticamente ao fim da função }
void ponteiroTradicional() { // Utilizando ponteiro tradicional (ponteiro bruto) Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); // Utilize pSong... // Exemplo: pSong->duration(); // Não esqueça de deletar o ponteiro delete pSong; }
Exemplo modificado de: https://msdn.microsoft.com/pt-br/library/hh279674.aspx
Códigos não utilizados devem ser deletados
Códigos não mais utilizados (comentados) devem ser deletados, por exemplo:
bool equal( int value1, int value2 ) { /* if( value1 < value2 || value1 > value2 ) { return false; } else { return true; } */ // Compara dois valores e retorna // verdadeiro se os valores são iguais if( value1 == value2 ) { return true; } return false; }
bool equal( int value1, int value2 ) { // Compara dois valores e retorna // verdadeiro se os valores são iguais if( value1 == value2 ) { return true; } return false; }
Assim o código fica mais limpo e mais fácil de compreender.
Evite métodos com muitos parâmetros
Sempre que possível evite a utilização de muitos parâmetros em métodos. Métodos com muitos parâmetros são geralmente difíceis de compreender. Se necessário refatore o método.
void showUserInformation(string firstName, string lastName, string gender, int age, double height, double weight);
// Onde 'User' é um objeto/estrutura de dados void showUserInformation(User &user);
Utilize espaços em branco para melhor visualização
Utilize espaços em branco para melhor visualização, por exemplo:
if( (majorVersion == 2 && minorVersion == 5) || majorVersion >= 3 )
if((majorVersion==2 && minorVersion==5) || majorVersion>=3)
if((majorVersion==2&&minorVersion==5)||majorVersion>=3)
Limite o escopo das variáveis
Sempre que possível limite o escopo das variáveis:
for (int i = 0; i < 15; ++i) { MyObject obj(i); // Faça algo com obj }
MyObject obj; // inicialização de objeto sem sentido for (int i = 0; i < 15; ++i) { obj = MyObject(i); // operação de atribuição desnecessária // Faça algo com obj } // obj ainda está ocupando memória sem motivo
Prefira ++i
em vez de i++
Ainda que i++
seja semanticamente correto, o pré-incremento (++i
) é mais rápido que pós-incremento (i++
), uma vez que não requer uma cópia do objeto.
for (int i = 0; i < 15; i++) { std::cout << i << '\n'; }
for (int i = 0; i < 15; ++i) { std::cout << i << '\n'; }
Mesmo que os compiladores mais modernos otimizem esses dois laços para o mesmo código assembly, a utilização de ++i
ainda é uma boa prática.
Pare e dê uma volta
Sempre que estiver empacado na solução de um problema, respire fundo e vá dar uma volta ou fazer alguma outra atividade por um certo período de tempo. Isso ajuda a esfriar um pouco a cabeça e pensar em uma solução mais claramente.