- Introdução
- Estilo de Código
2.1. Nomes de Variáveis
2.2. Nomes de Constantes
2.3. Nomes de Funções
2.4. Nomes de Classes
2.5. Comentários
2.6. Indentação
2.7. Não utilize números mágicos
2.8. Inclua guards
2.9. Sempre utilize chaves
2.10. Mantenha as linhas com um comprimento razoável
2.11. Utilize aspas duplas para incluir arquivos locais
2.12. Utilize constantes sempre que possível
2.13. Passe ou retorne tipos simples por valor
2.14. Utilize double em vez de float - Dicas
3.1. Lembre-se de deletar os ponteiros
3.2. Utilize ponteiros inteligentes
3.3. Códigos não utilizados devem ser deletados
3.4. Evite métodos com muitos parâmetros
3.5. Utilize espaços em branco para melhor visualização
3.6. Limite o escopo das variáveis
3.7. Prefira++i
em vez dei++
3.8. Pare e dê uma volta - Referências
Este documento apresenta um guia rápido de boas práticas em C++ e é voltado para iniciantes na linguagem mas também pode ser útil para desenvolvedores intermediários.
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 criar issues ou enviar pull requests.
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.
Variáveis devem sempre começar com letra minúscula, por exemplo:
✔️ Bom
string myWeirdVariable;
// ou
string my_weird_variable;
❌ Ruim
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.
Constantes devem ser declaradas sempre em letras maiúsculas (caixa alta):
✔️ Bom
const double PI = 3.14159;
❌ Ruim
const double pi = 3.14159;
Nomes de funções devem começar com a primeira letra minúscula, assim como as variáveis:
✔️ Bom
void myFunction();
❌ Ruim
void MyFunction();
❌ Pior ainda
void My_Function();
Nomes de classes devem começar com a primeira letra maiúscula e seguir o padrão CamelCase (preferencialmente):
✔️ Bom
class LinkedList
❌ Ruim
class linkedList
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:
✔️ Bom
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:
❌ Ruim
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 /* */
.
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', por exemplo:
❌ Ruim
double calc( double value )
{
return value * 3.14159;
}
Nestes casos opte por definir uma constante, por exemplo:
✔️ Bom
const double PI = 3.14159;
double calc( double value )
{
return value * PI;
}
Mas utilize, SIM, números, quando isso fizer sentido, por exemplo:
✔️ Bom
double calc( double value )
{
return value * 2;
}
❌ Ruim
#define TWO 2
double calc( double value )
{
return value * TWO;
}
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:
✔️ Bom
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
void myFunc();
};
#endif
❌ Ruim
class MyClass
{
public:
void myFunc();
};
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:
✔️ Bom
int sum = 0;
for (int i = 0; i < 15; ++i)
{
++sum;
std::cout << i << std::endl;
}
❌ Ruim
for (int i = 0; i < 15; ++i)
std::cout << i << std::endl;
❌ Erro semântico
int sum = 0;
for (int i = 0; i < 15; ++i)
++sum;
std::cout << i << std::endl;
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:
❌ Ruim
if( (x == 1 && y == 2 && myFunction() == true) || (x == 0 && y == 0 && myFunction() == false) )
{
}
✔️ Bom
if( (x == 1 && y == 2 && myFunction() == true) ||
(x == 0 && y == 0 && myFunction() == false) )
{
}
Utilize aspas duplas (""
) para incluir arquivos locais.
❌ Ruim
#include <string>
#include <MyHeader.hpp>
✔️ Bom
#include <string>
#include "MyHeader.hpp"
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.
❌ Ruim
class MyClass
{
public:
void do_something(int i);
void do_something(std::string str);
};
✔️ Bom
class MyClass
{
public:
void do_something(const int i);
void do_something(const std::string &str);
};
Não passe ou retorne tipos simples por referência, mas sim por valor:
❌ Ruim
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
.
✔️ Bom
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.
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++.
Nesta seção você irá encontrar algumas dicas importantes que podem ser úteis durante o desenvolvimento.
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).
✔️ Bom
double myFunction(double value1, double value2)
{
Calculator *calc = new Calculator();
double result = calc->sum(value1, value2);
delete calc;
calc = NULL;
return result;
}
❌ Ruim
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.
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).
✔️ Bom
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
}
❌ Ruim
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 mais utilizados (comentados) devem ser deletados, por exemplo:
❌ Ruim
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;
}
✔️ Bom
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.
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.
❌ Ruim
void showUserInformation(string firstName, string lastName, string gender, int age, double height, double weight);
✔️ Bom
// Onde 'User' é um objeto/estrutura de dados
void showUserInformation(User &user);
Utilize espaços em branco para melhor visualização, por exemplo:
✔️ Bom
if( (majorVersion == 2 && minorVersion == 5) || majorVersion >= 3 )
❌ Ruim
if((majorVersion==2 && minorVersion==5) || majorVersion>=3)
❌ Pior ainda
if((majorVersion==2&&minorVersion==5)||majorVersion>=3)
Sempre que possível limite o escopo das variáveis:
✔️ Bom
for (int i = 0; i < 15; ++i)
{
MyObject obj(i);
// Faça algo com obj
}
❌ Ruim
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
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.
❌ Ruim
for (int i = 0; i < 15; i++)
{
std::cout << i << '\n';
}
✔️ Bom
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.
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.
C++ Best Practices: https://www.gitbook.com/book/lefticus/cpp-best-practices/details
Google C++ Style Guide: https://google.github.io/styleguide/cppguide.html
10 most voted C++ best practices: http://codergears.com/Blog/?p=1957