The Beauty of C++

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:

 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.

Nomes de Constantes

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

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

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

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:

 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 /* */.

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:

 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;
}

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:

 Bom

#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass
{
public:
    void myFunc();
};

#endif

 Ruim

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:

 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

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

Utilize aspas duplas ("") para incluir arquivos locais.

 Ruim

#include <string>
#include <MyHeader.hpp>

 Bom

#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.

 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);
};

Passe ou retorne tipos simples por valor

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.

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).

 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.

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).

 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 utilizados devem ser deletados

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.

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.

 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

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)

Limite o escopo das variáveis

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

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.

 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.

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.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *