- 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:
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.
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 devem começar com a primeira letra minúscula, assim como as variáveis:
void myFunction();
void MyFunction();
void My_Function();
Nomes de classes devem começar com a primeira letra maiúscula e seguir o padrão CamelCase (preferencialmente):
class LinkedList
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:
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 /* */
.
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:
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;
}
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 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. 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.
#include <string>
#include <MyHeader.hpp>
#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.
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);
};
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.
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).
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.
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 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.
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, por exemplo:
if( (majorVersion == 2 && minorVersion == 5) || majorVersion >= 3 )
if((majorVersion==2 && minorVersion==5) || majorVersion>=3)
if((majorVersion==2&&minorVersion==5)||majorVersion>=3)
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
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.
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