Direitos Autorais IBM Corporation 1987, 2004. Todos os Direitos Reservados.

A palavra "Rational" e os produtos Rational são marcas registradas da Rational Software Corporation. As referências a outras empresas e seus produtos utilizam marcas de propriedade de suas respectivas empresas e são apenas para fins de referência.

Esse documento foi preparado para o Rational Software Corp. by Luan Doan-Minh, da Calypso Software Inc., Vancouver, B.C., Canada.


Conteúdo

Introdução

Princípios Fundamentais
Premissas
Classificação das Diretrizes
A Primeira e a Última Diretriz

Organização do Código e Estilo

Estrutura do Código
Estilo do Código

Comentários

Nomenclatura

Geral
Espaços de Nomes
Classes
Funções
Objetos e Parâmetros da Função
Exceções
Diversos

Declarações

Espaços de Nomes
Classes
Funções
Tipos
Constantes e Objetos

Expressões e Instruções

Expressões
Instruções

Tópicos Especiais

Gerenciamento de Memória
Manipulação de Erros e Exceções

Portabilidade

Nomes de Caminhos
Representação de Dados
Conversões de Tipo

Reutilização

Problemas de Compilação

Resumo de Orientação

Requisitos ou Restrições
Recomendações
Dicas

Bibliografia


Capítulo 1

Introdução

Grandes projetos de software geralmente são conduzidos por equipes de desenvolvedores igualmente grandes. Para que o código produzido por grandes equipes tenha qualidade mensurável na amplitude do projeto, o código deve ser escrito de acordo com um padrão; e ser julgado com ele. Portanto, é importante que as equipes de grandes projetos estabeleçam um padrão de programação ou conjunto de diretrizes.

O uso de um padrão de programação também torna possível fazer o seguinte:

O objetivo desse texto é apresentar regras de programação C++, diretrizes e dicas (genericamente conhecidas como diretrizes) que podem ser utilizadas como base para um padrão. Isso é destinado aos engenheiros de software que trabalham em equipes de grandes projetos.

A versão atual está propositadamente focalizada na programação (embora, às vezes, seja difícil traçar a linha entre a programação e o design); as diretrizes de design serão incluídas em uma data posterior.

As diretrizes apresentadas abrangem os seguintes aspectos do desenvolvimento C++:

Elas devem ser coletadas a partir de uma grande base de conhecimento de indústria. (Consulte a bibliografia para as origens: autores e referências.) Elas são baseadas em:

A maioria é baseada em uma porção da primeira categoria e grandes doses da segunda e da terceira. Infelizmente, alguns também são baseados na última categoria; principalmente, porque a programação é uma atividade altamente subjetiva: não existindo nenhuma forma aceita "melhor" ou "correta" para codificar tudo.

Princípios Fundamentais

O código fonte C++ limpo e inteligível é o principal objetivo da maioria das regras e diretrizes: o código fonte limpo inteligível sendo o fator de maior contribuição para a confiabilidade do software e capacidade de manutenção. O que entende-se por código compreensível e limpo pode ser capturado nos três seguintes princípios fundamentais [Kruchten, 94].

Surpresa Mínima - Ao longo de sua existência, o código fonte é lido com mais freqüência do que escrito, especialmente as especificações. Em condições ideais, o código deve ser lido como uma descrição de idioma inglês daquilo que está sendo feito, com o benefício adicional que ele executa. Os programas são mais escritos para as pessoas do que para os computadores. O código de leitura é um processo mental complexo que pode ser facilitado pela uniformidade, também conhecido nesse guia como o princípio da surpresa mínima. Um estilo uniforme em um projeto inteiro é uma razão principal para que uma equipe de desenvolvedores de software concorde com os padrões de programação e não deve ser percebido como algum tipo de punição ou como um obstáculo à criatividade e à produtividade.

Ponto Único de Manutenção - Sempre que possível, uma decisão de projeto deve ser expressada em apenas um ponto na fonte e a maioria de suas conseqüências deve ser derivada programaticamente a partir desse ponto. As violações desse princípio representam grande risco à manutenção e à confiança, bem como ao entendimento.

Ruído Mínimo - Por fim, como uma grande contribuição à legibilidade, o princípio do ruído mínimo é aplicado. Ou seja, é feito um esforço para evitar a confusão do código fonte com o "ruído" visual: barras, caixas e outros textos com conteúdo de poucas informações ou informações que não contribuam para o entendimento do propósito do software.

A intenção pretendida das orientações aqui expressas, não deve ser excessivamente restritiva; mas sim tentar fornecer orientação para o uso correto e seguro dos recursos de linguagem. A chave para o bom software reside em:

Premissas

As diretrizes apresentadas aqui criam um pequeno número de premissas básicas:

Classificação da Diretriz

As diretrizes não são de igual importância; elas são medidas utilizando a seguinte escala:

Dica:ícone de dica

Uma diretriz identificada pelo símbolo acima é uma dica de que uma parte simples do aviso pode ser seguida ou ignorada com segurança.

Recomendação: Ícone de Ok com a Mão

Uma diretriz identificada pelo símbolo acima é uma recomendação geralmente baseada em fundamentos mais técnicos: encapsulamento, coesão, acoplamento, portabilidade ou reutilização podem ser afetados, bem como o desempenho em algumas implementações. As recomendações devem ser seguidas, a menos que haja uma boa justificativa para não segui-las.

Requisito ou Restrição: Ícone de Dedo Indicador

Uma diretriz identificada pelo símbolo acima é um requisito ou restrição; uma violação conduziria definitivamente ao código inválido, não confiável ou não portátil. Os requisitos ou as restrições não podem ser violadas sem uma concessão

A Primeira e a Última Diretriz

Ícone de Dedo IndicadorUtilize o senso comum

Quando você não puder localizar uma regra aplicável ou diretriz; quando uma regra não se aplicar claramente; ou quando tudo mais falhar: utilize o senso comum e verifique os princípios fundamentais. Essa regra substitui todas as outras. O senso comum é requerido mesmo quando existirem regras e diretrizes.


Capítulo 2

Organização do Código e Estilo

Esse capítulo fornece orientação sobre a estrutura do programa e o layout.

Estrutura do Código

Os sistemas grandes geralmente são desenvolvidos como inúmeros subsistemas funcionais menores. Os subsistemas em si geralmente são construídos a partir de inúmeros módulos de código. No C++, um módulo normalmente contém a implementação para um único, ou em raras ocasiões, conjunto deabstrações intensamente relacionadas. No C++, uma abstração normalmente é implementada como uma classe. Uma classe possui dois componentes distintos: uma interface visível aos clientes de classe, fornecendo uma declaração ou especificação dos recursos de classe e responsabilidades; e uma implementação da especificação declarada (definição de classe).

Semelhante à classe, um módulo também possui uma interface e uma implementação: a interface do módulo contém as especificações para as abstrações de módulo contidas (declarações de classe); e a implementação do módulo contém a implementação real das abstrações (definições de classe).

Na construção do sistema; os subsistemas também podem ser organizados em grupos colaborativos ou camadas para minimizar e controlar suas dependências.

Ícone de Ok com a MãoColoque as especificações de módulo e as implementações em arquivos separados

Uma especificação de módulo deve ser colocada em um arquivo separado de sua implementação-o arquivo de especificação é conhecido como cabeçalho. Uma implementação de módulo pode ser colocada em um ou mais arquivos de implementação.

Se uma implementação de módulo contiver funções seqüenciais extensivas, declarações comuns de implementação privada, código de teste ou código específico de plataforma, separe essas partes em seus próprios arquivos e nomeie cada arquivo após o conteúdo de sua parte.

Se os tamanhos do programa executável forem um problema, as funções raramente utilizadas também devem ser colocadas em seus próprios arquivos individuais.

Construa um nome de arquivo de parte da seguinte maneira:

Ícone de Ok com a MãoSelecione um único conjunto de extensões de nome de arquivo para distinguir os cabeçalhos dos arquivos de implementação

As extensões de nome de arquivo geralmente utilizadas são: .h, .H, .hh, .hpp e .hxx para os arquivos de cabeçalho; e .c, .C, .cc, .cpp e .cxx para implementações. Selecione um conjunto de extensões e utilize-as consistentemente.

Exemplo
SymaNetwork.hh  // A extensão ".hh" utilizada para designar
                // um cabeçalho de módulo "SymaNetwork".
SymaNetwork.cc  // A extensão ".cc" utilizada para designar
                // uma implementação de módulo "SymaNetwork". 
Notas

O papel de trabalho padrão do rascunho C++ também utiliza a extensão ".ns" para os cabeçalhos encapsulados por um espaço de nomes.

Ícone de Ok com a MãoEvite definir mais de uma classe por especificação do módulo

Apenas em raras ocasiões as várias classes devem ser colocadas juntas em um módulo e, em seguida, apenas se estiverem intensamente associadas (por exemplo, um contêiner e seu iterador). É aceitável colocar a classe principal de um módulo e suas classes de suporte dentro do mesmo arquivo de cabeçalho, se todas as classes sempre precisarem ser visíveis a um módulo do cliente.

Fundamentos

Reduz a interface de um módulo e as dependências de outros sobre ela.

Ícone de Ok com a MãoEvite colocar as declarações de implementação privada nas especificações do módulo

Com exceção dos membros privados da classe, as declarações privadas de implementação de um módulo (por exemplo, os tipos de implementação e classes de suporte) não devem aparecer na especificação do módulo. Essas declarações devem ser colocadas nos arquivos de implementação necessários, a menos que as declarações sejam necessárias por vários arquivos de implementação; nesse caso as declarações devem ser colocadas em um arquivo de cabeçalho particular secundário. O cabeçalho particular secundário deve, então, ser incluído por outros arquivos de implementação, conforme necessário.

Essa prática assegura que:

Exemplo
// Especificação do módulo foo, contida no arquivo "foo.hh"
//
class foo
{
.. declarations
};
// Fim de "foo.hh"
// Declarações privadas para o módulo foo, contidas no arquivo
// "foo.private.hh" e utilizadas por todos os arquivos de implementação foo.
... private declarations
// Fim de "foo.private.hh"
// Implementação foo do módulo, contida em vários arquivos
// "foo.x.cc" e "foo.y.cc"
// Arquivo "foo.x.cc"
//
#include "foo.hh" // Include module's own header
#include "foo.private.hh" // Include implementation 
// declarações requeridas.
... definitions
// Fim de "foo.x.cc"
// Arquivo "foo.y.cc"
//
#include "foo.hh"
#include "foo.private.hh"
... definitions
// End of "foo.y.cc"            

Ícone de Dedo IndicadorSempre utilize #include para obter acesso à especificação de um módulo

Um módulo que utiliza outro módulo deve utilizar a diretiva #include do pré-processador para adquirir visibilidade da especificação do módulo do fornecedor. De forma correspondente, os módulos nunca devem redeclarar qualquer parte da especificação do módulo de um fornecedor.

Ao incluir os arquivos, apenas utiliza a sintaxe #include <header> para os cabeçalhos "standard"; utilize a sintaxe #include "header" para o restante.

O uso da diretiva #include também se aplica aos próprios arquivos de implementação de um módulo: uma implementação de módulo deve incluir sua própria especificação e os cabeçalhos secundários privados (consulte "Colocar Especificações de Módulo e Implementações em Arquivos Separados").

Exemplo
// A especificação do módulo foo em seu arquivo de cabeçalho
// "foo.hh"
//
class foo
{
... declarations
};
// Fim de "foo.hh"

// A implementação do módulo foo no arquivo "foo.cc"
//
#include "foo.hh" // The implementation includes its own
                             // especificação
... definições para os membros foo
// Fim de "foo.cc"            

Uma exceção à regra #include ocorre quando um módulo apenas utiliza ou contém os tipos de um módulo do fornecedor (classes) por referência (utilizando o ponteiro ou declarações de tipo de referência); nesse caso o uso por referência ou detenção é especificado utilizando uma declaração de redirecionamento (consulte também "Minimizar Dependências de Compilação") em vez de uma diretiva #include.

Evite incluir mais do que é absolutamente necessário: isso significa que os cabeçalhos do módulo não devem incluir outros cabeçalhos que são requeridos apenas pela implementação do módulo.

Exemplo
#include "a_supplier.hh"

class needed_only_by_reference;// Utilize uma declaração de redirecionamento
                               // para uma classe se só precisarmos de
                               // um ponteiro ou um acesso de referência
                               // a ele.
void operation_requiring_object(a_supplier required_supplier, ...);
//
// A operação que requer um objeto real do fornecedor; portanto, a
// especificação do fornecedor deve ser #incluída.

void some_operation(needed_only_by_reference& a_reference, ...);
//
// Alguma operação que precisa apenas de uma referência a um objeto; portanto,
// deve ser utilizada uma declaração de redirecionamento para o fornecedor.
Fundamentos

Essa regra assegura que:

Ícone de Ok com a MãoColoque as definições de função seqüencial do módulo em um arquivo separado

Quando um módulo tiver várias funções seqüenciais, suas definições devem ser colocadas em um arquivo separado de função-seqüencial-única. O arquivo de função seqüencial deve ser incluído ao final do arquivo de cabeçalho do módulo.

Consulte também "Utilizar um Símbolo de Compilação Condicional No_Inline para Contestar a Compilação Seqüencial".

Fundamentos

Essa técnica impede que os detalhes de implementação misturem o cabeçalho de um módulo; portanto, preservando uma especificação limpa. Isso também ajuda na redução da replicação do código quando não estiver compilando seqüencialmente: utilizando uma compilação condicional, as funções seqüenciais podem ser compiladas em um único arquivo de objeto em oposição à compilação estática em cada módulo sendo utilizado. De forma correspondente, as definições de função seqüencial não devem ser definidas nas definições de classe, a menos que sejam absolutamente triviais.

Ícone de Ok com a MãoQuebre grandes módulos em várias unidades de conversão, se o tamanho do programa for uma preocupação

Quebre os grandes módulos em várias unidades de conversão para facilitar a remoção de código não referenciado durante o link do programa. As funções do membro que raramente são referenciadas devem ser segregadas em arquivos separados desses que geralmente são utilizados. No extremo, as funções membro individuais podem ser colocadas em seus próprios arquivos [Ellemtel, 1993].

Fundamentos

Os linkers não são igualmente capazes de eliminar o código não referenciado dentro de um arquivo de objeto. Quebrar os grandes módulos em vários arquivos permite que esses linkers reduzam os tamanhos do programa executável, eliminando o link dos arquivos inteiros de objetos [Ellemtel, 1993].

Notas

Também pode ser válido considerar primeiro se o módulo deve ser quebrado em abstrações menores.

Ícone de Ok com a MãoIsole as dependências de plataforma

Separe o código dependente de plataforma do código independente de plataforma; isso facilitará a portabilidade. Os módulos dependentes de plataforma devem ter nomes de arquivos qualificados por seus nomes de plataforma para realçar a dependência de plataforma.

Exemplo
SymaLowLevelStuff.hh         // "LowLevelStuff" 
                             // especificação
SymaLowLevelStuff.SunOS54.cc // Implementação SunOS 5.4
SymaLowLevelStuff.HPUX.cc    // Implementação HP-UX
SymaLowLevelStuff.AIX.cc     // Implementação AIX 
Notas

A partir de um ponto de vista da arquitetura e da manutenção, também é uma boa prática conter as dependências de plataforma em um pequeno número de subsistemas de baixo nível.

Adote uma estrutura de conteúdo de arquivo padrão e aplique-a consistentemente

Uma estrutura de conteúdo de arquivo sugerida consiste nas seguintes partes na seguinte ordem:

Fundamentos

A ordem do conteúdo de arquivo acima apresenta primeiro as informações pertinentes do cliente e é consistente com o racional para a ordem de seções públicas, protegidas e privadas de uma classe.

Notas

Dependendo da política corporativa, as informações de direitos autorais talvez precisem ser colocadas na parte superior do arquivo.

Ícone de Ok com a MãoProteja-se das inclusões repetidas de arquivo

A inclusão repetida de arquivo e a compilação devem ser evitadas utilizando a seguinte construção em cada arquivo de cabeçalho:

#if !defined(module_name) // Utilize os símbolos do pré-processador para
#define module_name       // se proteger das inclusões
                          // repetidas...
                          // As declarações ficam aqui
#include "module_name.inlines.cc" // Optional inline
                                  // a inclusão fica aqui.
// Sem mais declarações após a inclusão das funções seqüenciais
// do módulo.
#endif // End of module_name.hh            

Utilize o nome de arquivo do módulo para o símbolo de proteção de inclusão. Utilize o mesmo tipo de letra utilizado para o nome do módulo.

Ícone de Ok com a MãoUtilize um símbolo de compilação condicional "No_Inline" para contestar a compilação seqüencial

Utilize a seguinte construção de compilação condicional para controlar a seqüência versus compilação fora de linha das funções seqüenciáveis:

// Na parte superior de module_name.inlines.hh
#if !defined(module_name_inlines)
#define module_name_inlines

#if defined(No_Inline)
#define inline // Anular palavra-chave seqüencial
#endif

... // As definições seqüenciais ficam aqui
#endif // End of module_name.inlines.hh

// No fim de module_name.hh
//
#if !defined(No_Inline)
#include "module_name.inlines.hh"
#endif

// Na parte superior de module_name.cc após a inclusão de
// module_name.hh
//
#if defined(No_Inline)
#include "module_name.inlines.hh"
#endif            

A construção de compilação condicional é semelhante a várias construções de proteção de inclusão. Se o símbolo No_Inline não for definido, as funções seqüenciais são compiladas com a especificação do módulo e automaticamente excluídas da implementação do módulo. Se o símbolo No_Inline for definido, as definições seqüenciais serão excluídas da especificação do módulo, mas incluídas na implementação de módulo com a palavra-chave inline anulada.

Fundamentos

A técnica acima permite a replicação de código reduzido quando as funções seqüenciais forem compiladas fora de linha. Ao utilizar a compilação condicional, uma única cópia das funções seqüenciais é compilada no módulo de definição; versus o código replicado, compilado como funções "estáticas" (link interno) em cada módulo de uso quando a compilação fora de linha for especificada por um comutador do compilador.

Notas

O uso da compilação condicional aumenta a complexidade envolvida na manutenção das dependências de construção. Essa complexidade é gerenciada tratando sempre dos cabeçalhos e das definições de função seqüencial como uma única unidade lógica: portanto, os arquivos de implementação dependem dos arquivos de definição de função do cabeçalho e seqüencial.

Estilo do Código

Ícone de Ok com a MãoUtilize um estilo de recuo pequeno consistente para instruções aninhadas

O recuo consistente deve ser utilizado para delinear visualmente as instruções aninhadas; o recuo entre 2 e 4 espaços foi comprovado por ser visualmente mais efetivo para esse propósito. Recomendamos utilizar um recuo regular de 2 espaços.

Os delimitadores ({}) de instrução de bloco ou compostos devem estar no mesmo nível de recuo das instruções vizinhas (por implicação, isso significa que {} estão alinhadas verticalmente). As instruções dentro do bloco devem ser recuadas pelo número escolhido de espaços.

Os etiquetas de caso de uma instrução switch devem estar no mesmo nível de recuo da instrução switch; as instruções dentro da instrução switch podem então ser recuadas por 1 nível de recuo da própria instrução switch e etiquetas de caso.

Exemplo
if (true)
{  		// Novo bloco
foo(); // Instrução(ões) no bloco
       // recuada(s) em 2 espaços.
}
else
{
bar();
}
while (expression)
{
statement();
}
switch (i)
{
case 1:
do_something();// Instruções recuadas por
               // 1 nível de recuo da
break;         // própria instrução switch.
case 2:
  //...
default:
  //...
}            
Fundamentos

Um recuo de 2 espaços consiste em permitir fácil reconhecimento dos blocos e permitir blocos aninhados suficientes antes que o código se movimente muito fora da borda direita de um monitor de exibição ou página impressa.

Ícone de Ok com a MãoRecue os parâmetros de função a partir do nome da função ou nome do escopo

Se uma declaração da função não couber em uma única linha, coloque o primeiro parâmetro na mesma linha do nome da função; e cada parâmetro subseqüente em uma nova linha, recuado no mesmo nível do primeiro parâmetro. Esse estilo de declaração e recuo, mostrado abaixo, deixa espaços em branco abaixo do tipo de retorno da função e nome; portanto, melhorando suas visibilidades.

Exemplo
void foo::function_decl( some_type first_parameter, 
                                     some_other_type second_parameter,
                                     status_type and_subsequent);                                          

Se seguir a diretriz acima provocar uma quebra de linha ou fizer com que os parâmetros fiquem com excessivo recuo, recue todos os parâmetros do nome da função ou nome escopo (classe, espaço do nome), sendo cada um em uma linha separada:

Exemplo
     void foo::function_with_a_long_name( // o nome da função é muito menos visível
                                     some_type first_parameter, 
                                     some_other_type second_parameter,
                                     status_type and_subsequent);                   

Consulte também as regras de alinhamento abaixo.

Ícone de Ok com a MãoUtilize um comprimento máximo de linha que caberia no tamanho do papel impresso padrão

O comprimento máximo das linhas do programa deve ser limitado para impedir a perda das informações quando impressas no tamanho de papel impresso padrão (carta) ou padrão.

Notas

Se o nível de recuo fizer com que as instruções profundamente aninhadas se movimentem muito para direita e as instruções se estendam muito além da margem direita, provavelmente esse será um bom momento para considerar a quebra do código em funções menores e mais gerenciáveis.

Ícone de Ok com a MãoUtilize a dobra de linha consistente

Quando as linhas de parâmetro nas declarações de função, definições e chamadas ou enumeradores em uma declaração enum não couber em uma única linha, quebre a linha após cada elemento da lista e coloque cada elemento em uma linha separada (consulte também "Recuar Parâmetros de Função do Nome da Função ou Nome do Escopo").

Exemplo
enum color { red, 
             orange, 
             yellow, 
             green, 
             //...
             violet
                   };            

Se uma declaração de gabarito de classe ou de função for excessivamente longa, dobre-a em linhas consecutivas após a lista de argumentos de gabarito. Por exemplo, (declaração da Biblioteca de Iteradores padrão, [X3J16, 95]):

template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n);            


Capítulo 3

Comentários

Esse capítulo fornece orientação sobre o uso de comentários no código.

Os comentários devem ser utilizados para complementar o código fonte, nunca o parafraseie:

  • Eles devem complementar o código fonte, explicando aquilo que não é óbvio; eles não devem duplicar a sintaxe do idioma ou as semânticas.
  • Eles devem ajudar o leitor a captar os conceitos de segundo plano, as dependências e as codificações de dados ou algoritmos especialmente complexos.
  • Eles devem realçar: os desvios da codificação ou padrões de design; o uso de recursos restritos; e os "truques".
  • Para cada comentário, o programador deve ser capaz de responder facilmente à pergunta: "Qual valor é incluído por esse comentário?" Geralmente, os nomes bem escolhidos eliminam a necessidade dos comentários. Os comentários, a menos que participem de alguma PDL (Program Design Language) formal, não são verificados pelo compilador; portanto, de acordo com o princípio de ponto-único-de-manutenção, as decisões de design devem ser expressas no código fonte em vez de serem expressas em comentários, mesmo nos gastos de algumas declarações a mais.

    Ícone de Ok com a MãoUtilize os comentários de estilo C++ em vez de comentários de estilo C

    O delimitador de comentário "//" de estilo C++ deve ser utilizado na preferência ao "/*...*/" de estilo C.

    Fundamentos

    Os comentários de estilo C++ são mais visíveis e reduzem o risco de comentar acidentalmente vastas extensões de código devido à falta de um delimitador de fim de comentário.

    Exemplo do Contador
    /* início do comentário com delimitador ausente de fim de comentário
    do_something();
    do_something_else(); /* Comente do_something_else */
                                  // O fim do comentário é aqui ---> */
                                  // do_something e
                                  // do_something_else
                                  // foram comentados acidentalmente!
    Do_further();      

    Ícone de Ok com a MãoMaximize a proximidade do comentário para o código fonte

    Os comentários devem ser colocados próximo ao código sobre os quais estão sendo comentados; com o mesmo nível de recuo e anexados ao código utilizando uma linha de comentário em branco.

    Os comentários que se aplicam a várias instruções sucessivas de origem devem ser colocados acima das instruções-servindo como uma introdução para as instruções. Da mesma forma, os comentários associados às instruções individuais devem ser colocados abaixo das instruções.

    Exemplo
    // Um comentário de pré-instruções aplicável
    // e inúmeras das seguintes instruções
    // 
    ...
    void function();
    //
    // Um comentário de pós-instrução para
    // a instrução precedenten. 

    Ícone de Ok com a MãoEvite os comentários de fim de linha

    Evite comentários na mesma linha de uma construção de origem: eles geralmente ficam desalinhados. No entanto, esses comentários são tolerados para descrições dos elementos em longas declarações, como enumeradores em uma declaração enum.

    Ícone de Ok com a MãoEvite os cabeçalhos de comentário

    Evite o uso dos cabeçalhos que contenham informações como autor, números de telefone, datas de criação e de modificação: o autor e os números de telefone se tornam obsoletos rapidamente; ao passo que as datas de criação e de modificação e as razões da modificação são mantidas de maneira mais eficiente por uma ferramenta de gerenciamento de configuração (ou alguma outra forma de arquivo de histórico de versão).

    Evite o uso das barras verticais, quadros ou caixas fechadas, mesmo para construções maiores (como funções e classes); eles devem incluir ruído visual e são difíceis de serem mantidos consistentes. Utilize as linhas em branco para separar os blocos relacionados do código fonte em vez das pesadas linhas de comentário. Utilize uma única linha em branco para separar as construções dentro das funções ou classes. Utilize as linhas duplas em branco para separar as funções entre si.

    Os quadros ou formulários podem ter a aparência de uniformidade e lembrar o programador para documentar o código, mas eles geralmente conduzem a um estilo de paráfrase [Kruchten, 94].

    Ícone de Ok com a MãoUtilize uma linha de comentário vazia para separar os parágrafos de comentário

    Utilize comentários vazios, em vez de linhas vazias, dentro de um único bloco de comentário para separar os parágrafos

    Exemplo
    // Alguma explicação aqui precisa de ser continuada
    // em um parágrafo subseqüente.
    //
    // A linha de comentário vazia acima torna
    // claro que esse é outro parágrafo
    // do mesmo bloco de comentário. 

    Ícone de Ok com a MãoEvite a redundância

    Evite repetir identificadores do programa em comentários e replicar as informações encontradas em qualquer lugar-em vez disso, forneça um ponteiro às informações. Caso contrário, qualquer mudança do programa pode precisar de manutenção em vários locais. E a falha ao efetuar as mudanças necessárias no comentário resultará em engano ou comentários errados: sendo que esses encerramentos serão piores do que não ter nenhum comentário.

    Ícone de Ok com a MãoEscreva o código de auto-documentação em vez dos comentários

    Sempre vise escrever o código de auto-documentação em vez de fornecer os comentários. Isso pode ser alcançado escolhendo nomes melhores; utilizando variáveis temporárias extras; ou reestruturando o código. Cuidado com o estilo, a sintaxe e a ortografia nos comentários. Utilize comentários do idioma natural em vez do estilo telegráfico ou oculto.

    Exemplo
    Substitua:
    do
    {
      ...
    } while (string_utility.locate(ch, str) != 0); 
    // Saia do loop de procura ao encontrá-lo.
    
    por:
    do
    {
      ...
    found_it = (string_utility.locate(ch, str) == 0);
    } while (!found_it);      

    Ícone de Ok com a MãoDocumente as classes e as funções

    Embora o código de auto-documentação seja preferido sobre os comentários; geralmente há uma necessidade de fornecer informações além de uma explicação das partes complicadas do código. As informações necessárias são a documentação de, no mínimo, o seguinte:

    Fundamentos

    A documentação do código em conjunto com as declarações deve ser suficiente para que um cliente utilize o código; a documentação é requerida desde que as semânticas completas das classes, funções, tipos e objetos não possam ser totalmente expressas utilizando o C++ sozinho.


    Capítulo 4

    Nomenclatura

    Este capítulo fornece orientação sobre a opção dos nomes para as diversas entidades C++.

    Geral

    Aparecer com bons nomes para as entidades do programa (classes, funções, tipos, objetos, literais, exceções e espaços de nomes) não é uma tarefa fácil. Para aplicativos de médio a grande porte, o problema se torna ainda mais desafiador: aqui o nome entra em conflito e a falta de sinônimos para designar conceitos distintos, mas semelhantes, acrescenta o grau de dificuldade.

    Utilizar uma convenção de nomenclatura pode diminuir o esforço mental requerido para inventar nomes adequados. Além desse benefício, uma convenção de nomenclatura possui o benefício adicional de reforçar a consistência no código. Para ser útil, a convenção de nomenclatura deve fornecer orientação sobre: o estilo tipográfico (ou como gravar os nomes); e a construção do nome (ou como escolher os nomes).

    Ícone de Ok com a MãoEscolha uma convenção de nomenclatura e aplique-a consistentemente

    Não é tão importante qual convenção de nomenclatura será utilizada contanto que seja aplicada consistentemente. A uniformidade na nomenclatura é muito mais importante do que a convenção real: a uniformidade suporta o princípio da surpresa mínima.

    Como o C++ é uma linguagem que faz distinção entre maiúsculas e minúsculas e como inúmeras convenções de nomenclatura distintas estão amplamente em uso pela comunidade C++; raramente será possível alcançar a consistência de nomenclatura absoluta. Recomendamos selecionar uma convenção de nomenclatura para o projeto com base no ambiente de host (por exemplo, UNIX ou Windows) e as bibliotecas de princípio utilizadas pelo projeto; para maximizar a consistência do código:

    Notas

    O leitor cuidadoso observará que os exemplos nesse texto atualmente não seguem todas as orientações. Isso é atribuído em parte ao fato de os exemplos serem derivados de várias origens; e também devido ao desejo de conservar o papel; portanto, as orientações de formatação não foram minuciosamente aplicadas. Mas, a mensagem é "faça o que eu digo, mas não o que eu faço".

    Ícone de Dedo IndicadorNunca declare nomes que comecem com um ou mais sublinhados ('_')

    Os nomes com um único sublinhado inicial ('_') são utilizados geralmente pelas funções de biblioteca ("_main" e "_exit"). Os nomes com sublinhados iniciais duplos ("__"); ou um único sublinhado inicial seguido de uma letra maiúscula são reservados para uso interno do compilador.

    Além disso, evite nomes com sublinhados adjacentes, uma vez que geralmente é difícil discernir o número exato de sublinhados.

    Ícone de Ok com a MãoEvite utilizar nomes de tipo que sejam diferentes apenas por tipo de letra

    É difícil lembrar as diferenças entre os nomes de tipo que diferem apenas por tipo de letra e, portanto, que são fáceis de serem confundidos entre si.

    Ícone de Ok com a MãoEvite o uso de abreviações

    As abreviações podem ser utilizadas se forem utilizadas comumente no domínio do aplicativo (por exemplo, FFT para Fast Fourier Transform), ou se forem definidas em uma lista reconhecida pelo projeto de abreviações. Caso contrário, é muito provável que ocorram aqui abreviações semelhantes, mas não idênticas, introduzindo problemas e erros posteriormente (por exemplo, track_identification sendo abreviado como trid, trck_id, tr_iden, tid, tr_ident e assim por diante).

    Ícone de Ok com a MãoEvite o uso de sufixos para denotar as construções de idioma

    O uso dos sufixos para categorizar os tipos de entidades (como type para o tipo, e error para as exceções) geralmente não é muito efetivo para transmitir o entendimento do código. Sufixos como array e struct também implicam uma implementação específica; que, no caso de uma mudança na implementação (mudando a representação de uma struct ou array) teria um efeito adverso após qualquer código cliente ou seria equivocado.

    No entanto, os sufixos podem ser úteis em inúmeras situações limitadas:

    Ícone de Ok com a MãoEscolha nomes limpos, legíveis e significativos

    Escolha nomes da perspectiva de uso; e utilize os adjetivos com nomes para aprimorar o significado local (específico de contexto). Além disso, certifique-se de que os nomes concordam com seus tipos.

    Escolhas os nomes de modo que construções como:

    object_name.function_name(...);
    object_name->function_name(...);      

    sejam fáceis de serem lidos e pareçam significativos.

    A velocidade da digitação não é uma justificativa aceitável para utilizar nomes resumidos ou abreviados. Os identificadores de uma letra ou abreviados geralmente são uma indicação de opção fraca ou lentidão. As exceções são instâncias bem-reconhecidas como o uso de E para a base dos logaritmos naturais; ou Pi.

    Infelizmente, os compiladores e as ferramentas de suporte, às vezes, limitam o comprimento dos nomes; portanto, deve se tomar cuidado para assegura que nomes extensos não sejam diferentes apenas por seus caracteres finais: os caracteres de diferenciação devem ser truncados por essas ferramentas.

    Exemplo
    void set_color(color new_color)
    {
      ...
    the_color = new_color;
      ...
    }
    is better than:
    void set_foreground_color(color fg)
    
    and:
    oid set_foreground_color(color foreground);{
      ...
    the_foreground_color = foreground;
      ...
    }      

    A nomenclatura no primeiro exemplo é superior aos outros dois: new_color é qualificada e concorda com seu tipo; através disso, reforçando as semânticas da função.

    No segundo caso, o leitor intuitivo poderia inferir que fg pretendia significar primeiro plano; no entanto, em qualquer estilo de programação, nada deve ser deixado para a intuição ou dedução do leitor.

    No terceiro caso, quando um parâmetro foreground for utilizado (longe de sua declaração), o leitor será levado a acreditar que foreground significa, na realidade, a cor do primeiro plano. No entanto, de forma concebível, poderia ter sido de qualquer tipo implicitamente conversível para uma color.

    Notas

    Formar os nomes a partir de substantivos e adjetivos, e assegurar que os nomes concordem com seus tipos segue a linguagem natural e aprimora a legibilidade do código e as semânticas.

    Ícone de Ok com a MãoUtilize a ortografia correta nos nomes

    As partes dos nomes que são palavras em inglês devem ser escritas corretamente e em conformidade com a forma requerida pelo projeto, ou seja, consistentemente em inglês ou americano, mas não nas duas formas. Isso é igualmente verdadeiro para os comentários.

    Ícone de Ok com a MãoUtilize as cláusulas de predicado positivo para Booleanos

    Para os objetos Booleanos, as funções e argumentos de função, utilize uma cláusula de predicado na forma positiva, por exemplo, found_it, is_available, mas não is_not_available.

    Fundamentos

    Ao negar os predicados, os negativos duplos são difíceis de serem entendidos.

    Espaços de Nomes

    Ícone de Ok com a MãoUtilize os espaços de nome para particionar os nomes globais em potencial por subsistemas ou por bibliotecas

    Se um sistema for decomposto em subsistemas, utilize os nomes de subsistemas como os nomes de espaço de nomes para particionar e minimizar o espaço de nome global do sistema. Se o sistema for uma biblioteca, utilize um único espaço de nome mais externo para a biblioteca inteira.

    Atribua a cada subsistema ou espaço de nome de biblioteca um nome significativo; além disso, atribua a ele um alias abreviado ou alias de acrônimo. Escolha aliases abreviados ou de acrônimos que não tenham tendência a entrarem em conflito, por exemplo, a biblioteca padrão do rascunho ANSI C++ [Plauger, 95] define std como o alias para iso_standard_library.

    Se o compilador ainda não suportar a construção do espaço de nomes, utilize os prefixos de nome para simular os espaços de nome. Por exemplo, os nomes públicos na interface de um subsistema de gerenciamento de sistemas poderiam ser prefixados com syma (abreviação para Gerenciamento do Sistema).

    Fundamentos

    Utilizar os espaços de nome para cercar os nomes potencialmente globais ajuda a evitar as colisões de nomes quando o código for desenvolvido independentemente (por equipes de subprojetos ou fornecedores). Uma conclusão é que apenas os nomes de espaço de nomes são globais.

    Classes

    Ícone de Ok com a MãoUtilize substantivos ou frases substantivas para os nomes de classe

    Utilize um substantivo ou uma frase substantiva comuns no singular, para atribuir a uma classe um nome que expresse sua abstração. Utilize nomes mais gerais para as classes básicas e nomes mais especializados para as classes derivadas.

    typedef ... reference; // A partir da biblioteca padrão
    typedef ... pointer;   // A partir da biblioteca padrão
    typedef ... iterator;  // A partir da biblioteca padrão
    class bank_account {...};
    class savings_account : public bank_account {...};
    class checking_account : public bank_account {...};      

    Quando houver um conflito ou ausência de nomes adequados para ambos, objetos e tipos; utilize o nome simples para o objeto e inclua um sufixo como mode, kind, code, e assim por diante para o nome do tipo.

    Utilize uma forma plural ao expressar uma abstração que represente uma coleta de objetos.

    typedef some_container<...> yellow_pages;      

    Quando as semânticas adicionais forem requeridas apenas além de uma coleta de objetos, utilize o seguinte a partir da biblioteca padrão como padrões comportamentais e sufixos de nomes:

    Funções

    Ícone de Ok com a MãoUtilize os verbos para nomes de função de tipo de procedimento

    Utilize os verbos e as frases de ação para funções que não possuam valores de retorno (declarações de função com um tipo de retorno nulo) ou funções que retornam valor por parâmetros de referência ou ponteiro.

    Utilize nomes ou substantivos para funções que retornam apenas um único valor por tipo de retorno de função não nula.

    Para classes com operações comuns (um padrão de comportamento), utilize os nomes de operação retirados de uma lista de opções do projeto. Por exemplo: begin, end, insert, erase (operações do contêiner a partir da biblioteca padrão).

    Evite a mentalidade de nomenclatura "get" e "set" (funções de prefixos com "get" e "set"), especialmente para as operações públicas para os atributos de objeto de obtenção e configuração. A nomenclatura da operação deve permanecer na abstração da classe e na provisão do nível de serviço; os atributos de objeto de obtenção e configuração são detalhes de implementação de baixo nível que enfraquecem o encapsulamento, se forem tornados públicos.

    Utilize os adjetivos (ou particípios passados) para as funções que retornam um Booleano (predicados). Para os predicados, geralmente é útil incluir o prefixo is ou has antes que um substantivo torne a leitura do nome uma asserção positiva. Isso também será útil quando o nome simples já for utilizado para um objeto, nome do tipo ou literal de enumeração. Seja exato e consistente com respeito ao tempo verbal.

    Exemplo
    void insert(...);
    void erase(...);
    
    Name first_name();
    bool has_first_name();
    bool is_found();
    bool is_available();      

    Não utilize nomes negativos uma vez que isso pode resultar em expressões com duplas negações (por exemplo, !is_not_found); dificultando mais o entendimento do código. Em alguns casos, um predicado negativo também pode se tornar positivo sem alterar sua semântica, utilizando um antônimo, como "is_invalid" em vez de "is_not_valid".

    Exemplo
    bool is_not_valid(...);
    void find_client(name with_the_name, bool& not_found);
    
    Should be re-defined as:
    bool is_valid(...);
    void find_client(name with_the_name, bool& found);      

    Ícone de Ok com a MãoUtilize a sobrecarga de função quando for pretendido o mesmo significado geral

    Quando as operações tiverem o mesmo propósito pretendido, utilize a sobrecarga em vez de tentar encontrar sinônimos: isso minimiza o número de conceitos e variações de operações no sistema e, portanto, reduz sua complexidade geral.

    Ao sobrecarregar os operadores, certifique-se de que as semânticas do operador sejam preservadas; se o significado convencional de um operador não puder ser preservado, escolha outro nome para a função em vez de sobrecarregar o operador.

    Objetos e Parâmetros da Função

    Ícone de Ok com a MãoAumente os nomes dos elementos gramaticais para enfatizar o significado

    Para indicar exclusividade ou para mostrar que essa entidade é o principal foco da ação, coloque o prefixo do objeto ou do nome do parâmetro com "the" ou "this". Para indicar um objeto secundário, temporário, auxiliar, coloque nele o prefixo "a" ou "current":

    Exemplo
    void change_name( subscriber& the_subscriber,
    const subscriber::name new_name)
    {
      ...
    the_subscriber.name = new_name;
      ...
    }
    void update(subscriber_list& the_list,
    const subscriber::identification with_id,
    structure& on_structure,
    const value for_value);
    void change( object& the_object,
    const object using_object);      

    Exceções


    Ícone de Ok com a MãoEscolha nomes de exceção com um significado negativo

    Como as exceções devem ser utilizadas apenas para manipular as situações de erro, utilize um substantivo ou uma frase substantiva que transmita claramente uma idéia negativa:

    overflow, threshold_exceeded, bad_initial_value      

    Ícone de Ok com a MãoUtilize os adjetivos definidos pelo projeto para os nomes de exceção

    Utilize uma das seguintes palavras bad, incomplete, invalid, wrong, missing ou illegal a partir de uma lista concordada de projetos como parte do nome em vez de utilizar sistematicamente error ou exception, que não transmitem as informações específicas.

    Diversos

    Ícone de Ok com a MãoUtilize primeiras letras maiúsculas para o exponente de ponto flutuante e dígitos hexadecimais.

    A letra 'E' nos literais de ponto flutuante e os dígitos hexadecimais 'A' para 'F' sempre devem ser em maiúsculas.


    Capítulo 5

    Declarações

    Este capítulo fornece orientação sobre o uso e a forma de diversos tipos de declarações C++.

    Espaços de Nomes

    Antes da existência do recurso de espaço de nome na linguagem C++, havia apenas meios limitados para gerenciar o escopo do nome; conseqüentemente, o espaço de nome global se tornou ainda mais populado, conduzindo a conflitos que evitaram que algumas bibliotecas fossem utilizadas no mesmo programa. O novo recurso de linguagem de espaço de nomes resolve o problema de poluição de espaço de nome global.

    Ícone de Dedo IndicadorLimite as declarações globais apenas para espaços de nome

    Isso significa que apenas os nomes de espaço de nomes podem ser globais; todas as outras declarações devem estar dentro do espaço de algum espaço de nomes.

    Ignorar essa regra pode conduzir eventualmente à colisão de nomes.

    Ícone de Ok com a MãoUtilize um espaço de nomes para agrupar a funcionalidade não-classe

    Para o agrupamento lógico da funcionalidade não-classe (como uma categoria de classe), ou para funcionalidade com escopo muito maior do que uma classe, como uma biblioteca ou um subsistema; utilize um espaço de nome para unificar logicamente as declarações (consulte "Utilizar Espaços de Nome Para Particionar Nomes Potencialmente Globais por Subsistemas ou por Bibliotecas").

    Expresse o agrupamento lógico da funcionalidade no nome.

    Exemplo
    namespace transport_layer_interface { /* ... */ };
    namespace math_definitions { /* ... */ };      

    Ícone de Ok com a MãoMinimize o uso dos dados de escopo global e do espaço de nomes

    O uso de dados de escopo global e do espaço de nomes é contrário ao princípio de encapsulamento.

    Classes

    As classes são o design fundamental e a unidade de implementação no C++. Eles devem ser utilizados para capturar o domínio e as abstrações de design e como um mecanismo de encapsulamento para implementar os ADT (Abstract Data Types).

    Ícone de Ok com a MãoUtilize class em vez de struct para implementar os tipos de dados abstratos

    Utilize a chave de classe class em vez de struct para implementar uma classe-um tipo de dados abstrato.

    Utilize a chave de classe struct para definir estruturas de dados antigas simples (POD) como em C, especialmente ao fazer interface com o código C.

    Embora class e struct sejam equivalentes e possam ser utilizados de forma permutável, o class possui ênfase de controle de acesso padrão preferencial (privado) para melhor encapsulamento.

    Fundamentos

    Adotar uma prática consistente para distinguir entre class e struct introduz uma distinção semântica acima e além das regras de linguagem: a class se torna a construção principal para capturar as abstrações e o encapsulamento; enquanto que struct representa uma estrutura de dados pura que pode ser trocada em programas de linguagem de programação mista.

    Ícone de Ok com a MãoDeclare os membros de classe na ordem de acessibilidade decrescente

    Os especificadores de acesso em uma declaração de classe devem aparecer na ordem publica, protegida, privada.

    Fundamentos

    A ordem pública, protegida e privada das declarações do membro assegura que as informações de maior interesse para o usuário da classe sejam apresentadas primeiro, reduzindo assim, a necessidade para o usuário da classe navegar através de detalhes de implementação ou irrelevantes.

    Ícone de Ok com a MãoEvite declarar membros de dados públicos ou protegidos para tipos de dados abstratos

    O uso dos membros de dados públicos ou protegidos reduz o encapsulamento de uma classe e afeta a elasticidade de um sistema a ser alterado: os membros de dados públicos expõem uma implementação class' aos seus usuários; os membros de dados protegidos expões uma implementação class' a suas classes derivadas. Qualquer alteração nos membros de dados protegidos ou públicos de class' terá conseqüências sobre os usuários e as classes derivadas.

    Ícone de Ok com a MãoUtilize os atalhos para preservar o encapsulamento

    Essa orientação parece ser não intuitiva à primeira vista: a amizade expõe as partes privadas aos amigos; portanto, como pode ser preservado o encapsulamento? Nas situações em que as classes são altamente interdependentes e requerem conhecimento interno de cada um, é melhor manter a boa relação do que exportar os detalhes internos através da interface da classe.

    Exportar os detalhes internos como membros públicos concede acesso aos clientes que não são desejáveis. Exportar os membros protegidos concede acesso a descendentes potenciais, encorajando um design hierárquico que também não é desejável. A boa relação concede acesso privado seletivo sem reforçar uma restrição de subclasse; portanto, preservando o encapsulamento de todos, mesmo daqueles que requerem acesso.

    Um bom exemplo de uso da boa relação para preservar o encapsulamento é conceder a boa relação a uma classe de teste de amigos. A classe de teste de amigos, analisando a parte interna da classe, pode implementar o código de teste apropriado, mas posteriormente, a classe de teste de amigos pode ser eliminada do código fornecido. Portanto, nenhum encapsulamento será perdido nem codificado em adição ao código distribuível.

    Ícone de Ok com a MãoEvite fornecer definições de função nas declarações da classe

    As declarações de classe devem conter apenas as declarações de função e nunca as definições de função (implementações).

    Fundamentos

    Fornecer as definições de função em uma declaração de classe polui a especificação da classe com detalhes de implementação; tornando a interface de classe menos compreensível e mais difícil de ser lida; e aumenta as dependências de compilação.

    As definições de função nas declarações da classe também reduzem o controle sobre a seqüência da função (consulte também "Utilizar um Símbolo de Compilação Condicional No_Inline para Contestar a Compilação Seqüencial").

    Ícone de Dedo IndicadorSempre forneça um construtor padrão para as classes com construtores explicitamente declarados

    Para permitir o uso de uma classe em uma matriz, ou de qualquer um dos contêineres STL; uma classe deve fornecer um construtor padrão público ou permitir a geração de um pelo compilador.

    Notas

    Existe uma exceção à regra acima quando uma classe tiver um membro de dados não estáticos do tipo de referência, nesse caso geralmente não será possível criar um construtor padrão significativo. Portanto, é questionável utilizar uma referência para um membro de dados do objeto.

    Ícone de Dedo IndicadorSempre declare os construtores de cópia e os operadores de designação para as classes com os membros de dados do tipo de ponteiro

    Se necessário e não declarado explicitamente, o compilador gerará implicitamente um construtor de cópia e um operador de designação para uma classe. O construtor de cópia definido pelo compilador e o operador de designação implementam o que é conhecido geralmente na terminologia Smalltalk como "shallow-copy": explicitamente, cópia compreensiva de membro com cópia bit a bit para ponteiros. O uso do construtor de cópia gerado pelo compilador e operadores de designação padrão é garantido para fuga de memória.

    Exemplo
    // Adapted from [Meyers, 92].
    void f()
    {
    String hello("Hello");// Assume String is implemented
                          // with a pointer to a char
                          // array.
    { // Enter new scope (block)
    String world("World");
    world = hello;        // Assignment loses world's
                          // original memory
    }	// Destruct world upon exit from
    	// block;
    	// also indirectly hello
    String hello2 = hello; // Assign destructed hello to
                           // hello2
    }      

    No código acima, a memória que retém a cadeia "World" é perdida após a designação. Após sair do bloco interno, world é destruído; portanto, perdendo também a memória referenciada por hello. O hello destruído é designado ao hello2.

    Exemplo
    // Adapted from [Meyers, 1992].
    void foo(String bar) {};
    void f()
    {
    String lost = "String that will be lost!";
    foo(lost);
    }      

    No código acima, quando foo é chamado com o argumento lost, lost será copiado para o foo utilizando o construtor de cópia definido pelo compilador. Como lost é copiado com uma cópia bitwise do ponteiro para "String that will be lost!", após sair de foo, a cópia lost será destruída (assumindo que o destruidor será implementado corretamente para liberar memória) juntamente com a memória que retém "String that will be lost!"

    Ícone de Dedo IndicadorNunca declare novamente os parâmetros do construtor para ter um valor padrão

    Exemplo
    // Example from [X3J16, 95; section 12.8]
    class X {
    public:
    X(const X&, int);	// int parameter is not 
    				   // initialized
    				   // No user-declared copy constructor, thus 
    				   // compiler implicitly declares one.
    };
    // Deferred initialization of the int parameter mutates 
    // constructor into a copy constructor.
    //
    X::X(const X& x, int i = 0) { ... }      
    Fundamentos

    Um compilador que não vê uma assinatura de construtor de cópia "standard" em uma declaração de classe declarará implicitamente um construtor de cópia. No entanto, a inicialização adiada dos parâmetros padrão pode modificar um construtor no construtor de cópia: resultando em ambigüidade quando for utilizado um construtor de cópia. Portanto, qualquer uso de um construtor de cópia será mal formado por causa da ambigüidade [X3J16, 95; seção 12.8].

    Ícone de Dedo IndicadorSempre declare os destruidores como virtuais

    A menos que uma classe seja explicitamente designada como não derivável, seu destruidor sempre deve ser declarado virtual.

    Fundamentos

    A exclusão de um objeto de classe derivada através de um ponteiro ou referência para um tipo de classe base resultará no comportamento não definido, a menos que o destruidor de classe base tenha sido declarado como virtual.

    Exemplo
    // Estilo inválido utilizado para redução
    class B {
    public:
    B(size_t size) { tp = new T[size]; }
    ~B() { delete [] tp; tp = 0; }
      //...
    private:
    T* tp;
    };
    
    class D : public B {
    public:
    D(size_t size) : B(size) {}
    ~D() {}
      //... 
    };
    
    void f()
    {
    B* bp = new D(10);
    delete bp; // Comportamento não definido devido ao
    		    // destruidor de classe base
    	       // não virtual
    }      

    Ícone de Ok com a MãoEvite declarar vários operadores de conversão e construtores únicos de parâmetro

    Os construtores de parâmetro únicos também podem ser impedidos de serem utilizados para conversão implícita, declarando-os com o especificador explicit.

    Ícone de Dedo IndicadorNunca redefina as funções não virtuais

    As funções não virtuais implementam o comportamento invariante e não são destinadas a serem especializadas por classes derivadas. Violar essa orientação pode produzir o comportamento inesperado: o mesmo objeto pode exibir diferente comportamento em diferentes momentos.

    As funções não virtuais são estaticamente ligadas; portanto, a função chamada após um objeto é regida pelo tipo estático da variável que faz referência a object-pointer-to-A e pointer-to-B respectivamente no exemplo abaixo-e não ao tipo real do objeto.

    Exemplo
    // Adapted from [Meyers, 92].
    class A {
    public:
    oid f(); // Non-virtual: statically bound
    };
    class B : public A {
    public:
    void f(); // Non-virtual: statically bound
    };
    void g()
    {
    B x;
    A* pA = &x; // Static type: pointer-to-A
    B* pB = &x; // Static type: pointer-to-B
    pA->f(); // Calls A::f
    pB->f(); // Calls B::f
    }      

    Ícone de Ok com a MãoUtilize as funções não-virtuais criteriosamente

    Como as funções não virtuais restringem as subclasses, ao restringir a especialização e o polimorfismo, cuidado para assegurar que uma operação seja verdadeiramente invariante para todas as subclasses antes de declará-la não-virtual.

    Ícone de Ok com a MãoUtilize os inicializadores construtores em vez das designações nos construtores

    A inicialização do estado de um objeto durante a construção deve ser executada pelo incializador construtor (uma lista do inicializador do membro) em vez de ser com operadores de designação dentro do corpo do construtor.

    Exemplo
    Do this:
    class X 
    {
    public:
    X();
    private
    Y the_y;
    };
    X::X() : the_y(some_y_expression) { } 
    //
    // "the_y" inicializado por um inicializador construtor
    
    Rather than this:
    X::X() { the_y = some_y_expression; }
    //
    // "the_y" initialized by an assignment operator.      
    Fundamentos

    A construção do objeto envolve a construção de todas as classes base e membros de dados antes da execução do corpo do construtor. A inicialização de membros de dados requer duas operações (construção mais designação), se executada em um corpo de construtor em oposição a uma única operação (construção com um valor inicial) quando executada utilizando um inicializador de construção.

    Para classes agregadas aninhadas maiores (classes que contêm classes que contêm classes...), os códigos extra de desempenho de várias operações-construção + designação de membros-podem ser significativos.

    Ícone de Dedo IndicadorNunca chame as funções de membro a partir de um inicializador construtor

    Exemplo
    class A
    {
    public:
    A(int an_int);
    };
    class B : public A
    {
    public:
    int f();
    B();
    };
    
    B::B() : A(f()) {} 
    // undefined: calls member function but A bas
    // not yet been initialized [X3J16, 95].      
    Fundamentos

    O resultado de uma operação será indefinido, se uma função-membro for chamada diretamente ou indiretamente a partir de um inicializador construtor antes que todos os inicializadores membro para as classes base tenham concluído [X3J16, 95].

    Ícone de Ok com a MãoCuidado ao chamar as funções de membro nos construtores e destruidores

    Cuidado ao chamar as funções membro nos construtores; fique atento pois mesmo se uma função virtual for chamada, aquele que for executado será o definido na classe do construtor ou destruidor ou em uma de suas bases.

    Ícone de Ok com a MãoUtilize static const para constantes de classe integral

    Ao definir as constantes de classe integral (inteiro), utilize os membros de dados static const em vez de #define ou de constantes globais. Se static const não for suportado pelo compilador, utilize enum em substituição.

    Exemplo
    Do this:
    class X {
    static const buffer_size = 100;
    char buffer[buffer_size];
    };
    static const buffer_size;
    
    Or this:
    class C {
    enum { buffer_size = 100 };
    char buffer[buffer_size];
    };
    
    But not this:
    #define BUFFER_SIZE 100
    class C {
    char buffer[BUFFER_SIZE];
    };      

    Funções

    Ícone de Ok com a MãoSempre declare um tipo de retorno de função explícita

    Isso evitará confusão quando o compilador reclamar de um tipo de retorno ausente para as funções declaradas sem um tipo de retorno explícito.

    Ícone de Ok com a MãoSempre forneça nomes de parâmetros formais nas declarações de função

    Além disso, utilize os mesmos nomes nas declarações de função e nas definições; isso minimizará as surpresas. Fornecer nomes de parâmetro aprimora a documentação do código e a capacidade de leitura.

    Ícone de Ok com a MãoEsforce-se para funções com um único ponto de retorno

    As instruções de retorno dispersas livremente sobre um corpo da função são comparáveis às instruções goto, dificultando a leitura e a manutenção do código.

    Vários retornos podem ser tolerados apenas em funções muito pequenas, quando todos os returns podem ser vistos simultaneamente e quando o código tiver uma estrutura muito regular:

    type_t foo()
    {
    if (this_condition)
    return this_value;
    else
    return some_other_value;
    }      

    As funções com o tipo de retorno nulo não devem ter instrução de retorno.

    Ícone de Ok com a MãoEvite criar função com efeitos colaterais globais

    A criação de funções que produzem efeitos colaterais globais (mude os dados não publicados em vez de seus estados de objetos internos: como dados globais e de espaço de nome) deve ser minimizada (consulte também "Minimizar o Uso de Dados Globais e Escopo de Espaço de Nome"). Mas se for inevitável, qualquer efeito colateral deve ser claramente documentado como parte da especificação da função.

    Transmitir os projetos requeridos como parâmetros torna o código menos dependente de contexto, mais robusto e mais fácil de ser entendido.

    Ícone de Ok com a MãoDeclare os parâmetros de função na ordem de volatilidade e importância decrescente.

    A order na qual os parâmetros são declarados é importante do ponto do vista do responsável pela chamada:

    Essa classificação permite tirar proveito dos padrões para reduzir o número de argumentos nas chamadas de função.

    Ícone de Ok com a MãoEvite declarar funções com um número variável de parâmetros

    Os argumentos para as funções com um número variável de parâmetros não podem ser verificados por tipo.

    Ícone de Ok com a MãoEvite declarar novamente as funções com parâmetros padrão

    Evite incluir padrões nas funções em novas declarações adicionais da função: além das declarações de redirecionamento, uma função deve ser declarada apenas uma vez. Caso contrário, isso pode causar confusão para os leitores que não estão cientes das declarações subseqüentes.

    Ícone de Ok com a MãoMaximize o uso de constantes nas declarações de função

    Verifique se as funções possuem algum comportamento constante (retorne um valor de constante; aceite os argumentos de constantes; ou opere sem efeitos colaterais) e expresse o comportamento utilizando o especificador const.

    Exemplo
    const T f(...); // Function returning a constant
                            // object.
    T f(T* const arg);	// Function taking a constant
    			          // pointer.
    // The pointed-to object can be
    // changed but not the pointer.
    T f(const T* arg);      // Function taking a pointer to, and
    T f(const T& arg);      // function taking a reference to a
                            // constant object. The pointer can
                            // change but not the pointed-to
                            // object.
    T f(const T* const arg);  // Function taking a constant
                              // pointer to a constant object.
                              // Neither the pointer nor pointed-
                              // to object may change.
    T f(...) const;  // Function without side-effect:
                     // does not change its object state;
                     // so can be applied to constant
                     // objects.      

    Ícone de Ok com a MãoEvite transmitir os objetos por valor

    Transmitir e retornar os objetos por valor pode incorrer em código extra do construtor e do destruidor. O código extra do construtor e do destruidor pode ser evitado transmitindo e retornando os objetos, por referência.

    As referências Const podem ser utilizadas para especificar se os argumentos transmitidos pela referência não podem ser modificadas. Os exemplos de uso típico são operadores de designação e construtores de cópia:

    C::C(const C& aC);
    C& C::operator=(const C& aC);      
    Exemplo

    Considere o seguinte:

    the_class the_class::return_by_value(the_class a_copy)
    {
    return a_copy;
    }
    the_class an_object;
    return_by_value(an_object);      

    Quando return_by_value for chamado com an_object como argumento, o construtor de cópia the_class é chamado para copiar an_object para a_copy. O construtor de cópia the_class é chamado novamente para copiar a_copy para o objeto temporário de retorno de função. O destruidor the_class é chamado para destruir a_copy após o retorno da função. Em algum tempo posteriormente, o destruidor the_class será chamado novamente para destruir o objeto retornado por return_by_value. O custo geral da chamada de função do-nothing é de dois construtores e de dois destruidores.

    A situação será ainda pior se the_class era uma classe derivada e continha dados de membro de outras classes; os construtores e destruidores das classes bases e classes contidas também seriam chamados; portanto, escalar o número de chamadas de construtores e destruidores incorridos pela chamada de função.

    Notas

    A orientação acima pode parecer convidar os desenvolvedores para sempre transmitirem e retornarem os objetos por referência; no entanto, deve-se tomar cuidado para não retornar referências aos objetos locais ou referências quando os objetos forem necessário. Retornar uma referência para um objeto local é um convite para o desastre após o retorno da função, a referência retornada tende a um objeto destruído!

    Ícone de Dedo IndicadorNunca retorne uma referência a um objeto local

    Os objetos locais são destruídos ao sair do escopo de função; utilizar objetos destruído é um convite ao desastre.

    Ícone de Dedo IndicadorNunca retorne um ponteiro de-referenciado inicializado com new

    A violação dessa orientação conduzirá a fugas de memória

    Exemplo
    class C {
    public:
      ...
    friend C& operator+( const C& left, 
    const C& right);
    };
    C& operator+(const C& left, const C& right)
    {
    C* new_c = new C(left..., right...);
    return *new_c;
    }
    C a, b, c, d;
    C sum;
    sum = a + b + c + d;      

    Como os resultados intermediários dos operadores + não são armazenados ao calcular a soma, os objetos intermediários não podem ser excluídos, conduzindo a fugas de memória.

    Ícone de Dedo IndicadorNunca retorne uma referência non-const ou ponteiro aos dados do membro

    A violação dessa orientação viola o encapsulamento de dados e pode conduzir a surpresas ruins.

    Ícone de Ok com a MãoUtilize as funções seqüenciais em preferência a #define para a expansão da macro

    Mas utilize inlining de forma sensata: apenas para funções bem pequenas; grandes funções inlining podem provocar a saturação do código.

    As funções seqüenciais também aumentam as dependências de compilação entre os módulos, uma vez que a implementação das funções seqüenciais precisam se tornar disponíveis para compilação do código do cliente.

    [Meyers, 1992] fornece uma discussão detalhada do seguinte, e não do exemplo extremo do uso incorreto da macro:

    Exemplo

    Não execute:

    #define MAX(a, b) ((a) > (b) ? (a) : (b))     

    Em vez disso, execute:

    inline int max(int a, int b) { return a > b ? a : b; }      

    A macro MAX possui inúmeros problemas: ela não é type-safe; e seu comportamento é não determinista:

    int a = 1, b = 0;
    MAX(a++, b);     // a é incrementado duas vezes
    MAX(a++, b+10);  // a é incrementado uma vez
    MAX(a, "Hello"); // comparando ints com ponteiros

    Ícone de Ok com a MãoUtilize os parâmetros padrão em vez da sobrecarga de função

    Utilize os parâmetros padrão em vez da sobrecarga de funções quando um único algoritmo puder ser explorado e o algoritmo puder ser colocado em parâmetro por um pequeno número de parâmetros.

    Utilizar os parâmetros padrão ajuda a reduzir o número de funções sobrecarregadas, aprimorando a capacidade de manutenção e reduz o número de argumentos necessários nas chamadas de função, aprimorando a capacidade de leitura do código.

    Ícone de Ok com a MãoUtilize a sobrecarga de função para expressar as semânticas comuns

    Utilize a sobrecarga de função quando várias implementações forem requeridas para a mesma operação semântica, mas com diferentes tipos de argumentos.

    Preserve o significado convencional ao sobrecarregar os operadores. Não se esqueça de definir os operadores relacionados, por exemplo operator== e operator!=.

    Ícone de Ok com a MãoEvite sobrecarregar as funções que possuem ponteiros e inteiros

    Evite sobrecarregar funções com um único argumento de ponteiro, por funções com um único argumento de inteiro:

    void f(char* p);
    void f(int i);      

    As seguintes chamadas podem provocar surpresas:

    f(NULL); f(0);  

    A resolução de sobrecarga resolve f(int) e não f(char*).

    Ícone de Dedo Indicador Faça com que operator= retorne uma referência para *this

    C++ permite o encadeamento das operações de designação:

    String x, y, z;
    x = y = z = "A string";      

    Como o operador de designação é associativo à direita, a cadeia "A string" é designada para z, z para y, e y para x. O operator= é chamado efetivamente uma vez para cada expressão à direita de =, em uma ordem da direta para a esquerda. Isso também significa que o resultado de cada operator= é um objeto; no entanto, é possível uma opção de retorno do objeto da mão esquerda ou da mão direita.

    Como a boa prática dita que a assinatura do operador de designação sempre deve ser no formato:

    C& C::operator=(const C&);      

    apenas o objeto da mão esquerda é possível (rhs é a referência const, lhs é a referência non-const); portanto, *this deve ser retornado. Consulte [Meyers, 1992] para obter uma discussão detalhada.

    Ícone de Dedo IndicadorFaça com que operator= verifique a auto-designação

    Existem duas boas razões para executar a verificação: primeiramente, a designação de um objeto de classe derivada envolve chamar o operador de designação de cada classe base até a hierarquia de herança e pular essas operações pode proporcionar significativas economias no tempo de execução. Em segundo lugar, a designação envolve a destruição do objeto "lvalue" antes de copiar o objeto "rvalue". No caso de uma auto-designação, o objeto rvalue é destruído antes de ser designado, o resultado da designação é então indefinida.

    Ícone de Ok com a MãoMinimize a complexidade

    Não grave funções excessivamente longas, por exemplo, com mais de 60 linhas de código.

    Minimize o número de instruções de retorno, 1 é o número ideal.

    Tente uma Complexidade Ciclomática inferior a 10 (soma das instruções de decisão + 1, para funções de instrução de saída simples).

    Tente uma Complexidade Ciclomática Estendida inferior a 15 (soma das instruções de decisão + operadores lógicos + 1, para funções únicas de instrução de saída).

    Minimize a extensão máxima da média da referência (distância em linhas entre a declaração de um objeto local e a primeira instância de seu uso).

    Tipo

    ícone de dicaDefina os tipos de sistema globais na amplitude do projeto

    Em grandes projetos, geralmente há uma coleta dos tipos utilizados freqüentemente em todo o sistema; nesse caso, é sensato coletar em conjunto esses tipos em um ou mais espaços de nome de utilitário global de baixo nível (consulte o exemplo para "Evitar o Uso de Tipos Fundamentais").

    Ícone de Ok com a MãoEvite o uso de tipos fundamentais

    Quando um alto grau de portabilidade for o objetivo, ou quando o controle for necessário sobre o espaço de memória ocupado por objetos numéricos ou quando um intervalo específico de valores for requerido; os tipos fundamentais não devem ser utilizados. Nessas situações é melhor declarar os nomes de tipo explícitos com restrições de tamanho que utilizam os tipos fundamentais apropriados.

    Certifique-se para que os tipos fundamentais não escapem de volta no código através de contadores de loop, índices de matriz e assim por diante.

    Exemplo
    namespace system_types {
    typedef unsigned char byte;
    typedef short int integer16; // 16-bit signed integer
    typedef int integer32; // 32-bit signed integer
    typedef unsigned short int natural16; // 16-bit unsigned integer
    typedef unsigned int natural32; // 32-bit unsigned integer
      ...
    }      
    Fundamentos

    A representação dos tipos fundamentais é dependente da implementação.

    ícone de dicaUtilize typedef para criar sinônimos para reforçar o significado local

    Utilize typedef para criar sinônimos para os nomes existentes, para fornecer nomes locais mais significativos e aprimorar a legibilidade (não há penalidade de tempo de execução para isso).

    typedef também pode ser utilizado para fornecer atalhos para os nomes qualificados.

    Exemplo
    // vector declaration from standard library
    //
    namespace std {
    template <class T, class Alloc = allocator>
    class vector {
    public:
    typedef typename 
    Alloc::types<T>reference reference;
    typedef typename 
    Alloc::types<T>const_reference const_reference;
    typedef typename 
    Alloc::types<T>pointer iterator;
    typedef typename 
    Alloc::types<T>const_pointer const_iterator;
      ...
    }
    }      

    Ao utilize typedef-names criados por typedef, não misture o uso do nome original e o sinônimo na mesma parte do código.

    Constantes e Objetos

    Ícone de Ok com a MãoEvite utilizar valores literais

    Utilize constantes nomeadas na preferência.

    Ícone de Ok com a MãoEvite utilizar a diretiva #define do pré-processador para definir as constantes

    Utilize const ou enum em substituição.

    Não execute:

    #define LIGHT_SPEED 3E8
    
    Em vez disso, execute:
    const int light_speed = 3E8;
    
    Ou isso, para as matrizes de redimensionamento:
    enum { small_buffer_size = 100, 
    large_buffer_size = 1000 };      
    Fundamentos

    A depuração é muito mais difícil porque os nomes introduzidos por #defines são substituídos durante o pré-processamento da compilação e não aparecem nas tabelas do símbolo.

    Ícone de Ok com a MãoDeclare os objetos próximos a seus pontos de primeiro uso

    Ícone de Ok com a MãoSempre inicialize os objetos const na declaração

    Os objetos const não declarados extern possuem link interno, inicializar esses objetos constantes na declaração permite que os inicializadores sejam utilizados no momento da compilação.

    Ícone de Dedo IndicadorNunca lance o "constness" de um objeto constante

    Os objetos constantes podem existir na memória de leitura.

    Ícone de Ok com a MãoInicialize os objetos na definição

    Especifique os valores iniciais nas definições do objeto, a menos que o objeto seja de auto-inicialização. Se não for possível designar um valor inicial significativo, designe um valor "nil" ou considere declarar o objeto posteriormente.

    Para grandes objetos, geralmente não é aconselhável construir os objetos e posteriormente inicializá-los utilizando a designação uma vez que isso pode ser muito oneroso (consulte também "Utilizar os Inicializadores do Construtor em Vez das Designações no Construtor").

    Se a inicialização apropriada de um objeto não for possível no momento da construção, inicialize o objeto utilizando um valor "nil" convencional que signifique "uninitialized". O valor nil deve ser utilizado apenas para inicialização para declarar um "unusable but known value" que possa ser rejeitado de uma forma controlada pelos algoritmos: para indicar um erro variável não inicializado quando o objeto for utilizado antes da inicialização apropriada.

    Observe que nem sempre é possível declarar um valor nil para todos os tipos, especialmente os tipos de módulo, como um ângulo. Nesse caso, escolha o valor menos provável.


    Capítulo 6

    Expressões e Instruções

    Este capítulo fornece orientação sobre o uso e a forma de diversos tipos de expressões e instruções do C++.

    Expressões

    ícone de dicaUtilize os parênteses redundantes para esclarecer as expressões compostas

    ícone de dicaEvite aninhar as expressões muito profundamente

    O nível de aninhamento de uma expressão é definido como o número de conjuntos aninhados de parênteses requeridos para avaliar uma expressão da esquerda para a direita, se as regras de precedência do operador forem ignoradas.

    Níveis demasiados de aninhamento dificultam a compreensão das expressões.

    Ícone de Dedo IndicadorNão assuma qualquer ordem de avaliação de expressão específica

    A menos que a ordem de avaliação seja especificada por um operador (operador de vírgula, expressão ternária e conjunções e disjunções); não assuma qualquer ordem de avaliação específica; assumir pode conduzir a surpresas desagradáveis e à não portabilidade.

    Por exemplo, não combine o uso de uma variável na mesma instrução de um incremento ou decremento da variável.

    Exemplo
    foo(i, i++);
    array[i] = i--;      

    ícone de dicaUtilize 0 para ponteiros nulos em vez de NULL

    O uso de 0 ou NULL para ponteiros nulos é um tópico altamente controvertido.

    C e C++ definem qualquer expressão constante com valor zero a ser interpretado como um ponteiro nulo. Como 0 é difícil de ser lido e o uso de literais é altamente desencorajado, os programadores utilizavam tradicionalmente a macro NULL como o ponteiro nulo. Infelizmente, não há definição portátil para NULL. Alguns compiladores ANSI C utilizavam (void *)0, mas isso se torna uma opção insuficiente para C++:

    char* cp = (void*)0; /* Legal C but not C++ */      

    Portanto, qualquer definição de NULL do formato (T*)0, em vez de zero simplesmente, requer um lançamento em C++. Historicamente, as diretrizes que defendem o uso de 0 para os ponteiros nulos, tentavam aliviar o requisito de lançamento e tornam o código mis portátil. No entanto, vários desenvolvedores C++ se sentem mais confortáveis utilizando NULL em vez de 0 e também argumentam que a maioria dos compiladores (mais precisamente, a maioria dos arquivos de cabeçalho) atualmente implementam NULL como 0.

    Essa orientação rege em favor de 0, uma vez que 0 tem a garantia de trabalhar independentemente do valor NULL; no entanto, devido à controvérsia, esse ponto é rebaixado para o nível de uma dica, a ser seguida ou ignorada, conforme adequado.

    Ícone de Dedo IndicadorNão utilize o lançamento do estilo antigo

    Utilize os novos operadores de lançamento (dynamic_cast, static_cast, reinterpret_cast, const_cast) em vez do lançamento de estilo antigo.

    Se você não possui novos operadores de lançamento; evite lançar em conjunto, especialmente para baixo (convertendo um objeto de classe base para um objeto de classe derivada).

    Utilize os operadores de lançamento da seguinte forma:

    Não utilize typeid para implementar a lógica type-switching: deixe que os operadores de lançamento executem a verificação de tipo e a conversão de forma atômica, consulte [Stroustrup, 1994] para obter uma discussão aprofundada.

    Exemplo

    Não execute o seguinte:

    void foo (const base& b)
    {
    if (typeid(b) == typeid(derived1)) {
    do_derived1_stuff();
    else if (typeid(b) == typeid(derived2)) {
    do_derived2_stuff();
    else if () {
    
      }
    }      
    Fundamentos

    O lançamento de estilo antigo destrói o sistema de tipo e pode conduzir a erros hard-to-detect que não são capturados pelo compilador: o sistema de gerenciamento de memória pode ser corrompido, as tabelas de função virtuais podem ser minimizadas e os objetos não relacionados podem ser danificado quando o objeto for acessado como um objeto de classe derivada. Observe que o dano pode ser feito mesmo por um acesso de leitura, uma vez que os ponteiros ou campos não existentes podem ser referenciados.

    Os operadores de lançamento de novo estilo tornam a conversão de tipo mais segura (na maioria dos casos) e mais explícita.

    Ícone de Dedo IndicadorUtilize o tipo de novo bool para as expressões Booleanas

    Não utilize as constantes ou macros Booleanas de estilo antigo: não há um valor Booleado padrão true; utilize o novo tipo bool, em substituição.

    Ícone de Dedo IndicadorNunca compare diretamente com o valor Booleano verdadeiro

    Como tradicionalmente não havia nenhum valor padrão para (1 ou ! 0); as comparações de expressões diferentes de zero para true poderiam falhar.

    Em vez disso, utilize as expressões Booleanas.

    Exemplo

    Evite executar o seguinte:

    if (someNonZeroExpression == true) 
    // May not evaluate to true
    
    O melhor é executar:
    if (someNonZeroExpression) 
    // Always evaluates as a true condition.      

    Ícone de Dedo IndicadorNunca compare os ponteiros aos objetos que não estejam dentro da mesma matriz

    O resultado dessas operações são quase sempre sem sentido.

    Ícone de Dedo IndicadorSempre designe um valor de ponteiro nulo para um ponteiro de objeto excluído

    Evite o desastre configurando um ponteiro para um objeto excluído como nulo: a exclusão repetida de um ponteiro não nulo é prejudicial, mas a exclusão repetida de um ponteiro nulo é inofensiva.

    Sempre designe um valor de ponteiro nulo após a exclusão mesmo antes de um retorno de função, uma vez que o novo código pode ser incluído posteriormente.

    Instruções

    Ícone de Ok com a MãoUtilize uma if-statement ao ramificar expressões Booleanas

    Ícone de Ok com a MãoUtilize uma switch-statement ao ramificar valores distintos

    Utilize uma instrução de comutação em vez de uma série de "else if" quando a condição de ramificação for um valor distinto.

    Ícone de Dedo IndicadorSempre forneça uma ramificação default para switch-statements para capturar os erros

    Uma instrução de comutação sempre deve conter uma ramificação default e a ramificação default deve ser utilizada para erros de interrupção.

    Essa política assegura que quando novos valores de comutação são introduzidos e as ramificações para manipular os novos valores são omitidos, a ramificação padrão existente capturará o erro.

    Ícone de Ok com a MãoUtilize um for-statement ou while-statement quando um teste pré-iteração for requerido em um loop

    Utilize um for-statement em preferência a uma instrução while quando a iteração e a terminação do loop forem baseadas no contador de loop.

    Ícone de Ok com a MãoUtilize um do-while-statement quando um teste pós-iteração for requerido em um loop

    Ícone de Ok com a MãoEvite o uso das instruções jump nos loops

    Evite sair (utilizando break, return ou goto) dos loops em vez da condição de terminação de loop; e pular de forma prematura para a próxima iteração com continue. Isso reduz o número de fluxo de caminhos de controle, facilitando a compreensão do código.

    Ícone de Dedo IndicadorNão utilize a instrução goto

    Isso parece ser uma orientação universal.

    Ícone de Ok com a MãoEvite o ocultamento de identificadores nos escopos aninhados

    Isso pode conduzir à confusão para os leitores e os riscos potenciais na manutenção.


    Capítulo 7

    Tópicos Especiais

    Este capítulo fornece orientação sobre os tópicos de gerenciamento de memória e relatório de erro.

    Gerenciamento de Memória

    Ícone de Dedo IndicadorEvite misturar as operações de memória C e C++

    A funções malloc, calloc e realloc da biblioteca C não devem ser utilizadas para alocar o espaço do objeto: o operador C++ novo deve ser utilizado para esse propósito.

    A única vez em que a memória deve ser alocada utilizando as funções C é quando a memória deve ser transmitida a uma função de biblioteca C para descarte.

    Não utilize excluir para liberar a memória alocada pelas funções C, ou para liberar os objetos criados por new.

    Ícone de Dedo IndicadorSempre utilize delete[] ao excluir os objetos de matriz criados por new

    Utilizar delete em objetos de matriz sem a notação de colchetes vazios ("[]") resultará apenas no primeiro elemento de matriz sendo excluído e, portanto, em fuga de memória.

    Manipulação de Erros e Exceções

    Como não foi obtida muita experiência ao utilizar o mecanismo de exceção C++, as orientações apresentadas aqui podem passar por significativa revisão futura.

    O padrão de rascunho C++ define duas amplas categorias de erros: erros lógicos e erros de tempo de execução. Os erros lógicos podem ser erros de programação evitáveis. Os erros de tempo de execução são definidos como erros que são atribuídos aos eventos que ultrapassam o escopo do programa.

    A regra geral para o uso das exceções é que o sistema na condição normal e na ausência de sobrecarga ou falha de hardware não deve lançar qualquer exceção.

    Ícone de Ok com a MãoUtilize as asserções de forma liberal durante o desenvolvimento para detectar os erros

    Utilize as asserções de pré e pós-condição da função durante o desenvolvimento para fornecer detecção de erro "drop-dead".

    As asserções fornecem um mecanismo de detecção de erro de provisão simples e útil até que o código de manipulação de erro final seja implementado. As asserções possuem o bônus adicional para que seja capaz de ser compilado utilizando o símbolo do pré-processador "NDEBUG" (consulte "Definir o Símbolo NDEBUG com um Valor Específico").

    A macro de asserção foi tradicionalmente utilizada para esse propósito; no entanto, a referência [Stroustrup, 1994] fornece uma alternativa de gabarito, consulte abaixo.

    Exemplo
    template<class T, class Exception>
    inline void assert ( T a_boolean_expression, 
    Exception the_exception)
    {
    if (! NDEBUG)
    if (! a_boolean_expression) 
    throw the_exception;
    }      

    Ícone de Ok com a MãoUtilize as exceções apenas para condições realmente excepcionais

    Não utilize as exceções para os eventos freqüentes antecipados: as exceções provocam interrupções no fluxo normal de controle do código; dificultando ainda mais o entendimento e a manutenção.

    Os eventos antecipados devem ser tratados no fluxo normal de controle do código; utilize um valor de retorno da função ou o código de status do parâmetro "out", conforme necessário.

    As exceções também não devem ser utilizadas para implementar as estruturas de controle: isso seria outra forma de instrução "goto".

    Ícone de Ok com a MãoDerive as exceções de projeto a partir das exceções padrão

    Isso assegura que todas as exceções suportem um conjunto mínimo de operações comuns e possam ser manipuladas por um pequeno conjunto de rotinas de tratamento de nível alto.

    Os erros lógicos (erro de domínio, erro de argumento inválido, erro de comprimento e erro fora do intervalo) devem ser utilizados para indicar os erros de domínio no aplicativo, os argumentos inválidos transmitidos às chamadas de função, construção de objetos além de seus tamanhos permitidos e valores de argumento que não estão dentro das faixas permitidas.

    Os erros de tempo de execução (erro de faixa e erro de estouro) devem ser utilizados para indicar erros aritméticos e de configuração, dados corrompidos ou erros de esgotamento de recursos detectáveis apenas no tempo de execução.

    Ícone de Ok com a MãoMinimize o número de exceções utilizadas por uma determinada abstração

    Em grandes sistemas, ter que manipular um grande número de exceções em cada nível dificulta a leitura e a manutenção do código. O processamento da exceção pode atrapalhar o processamento normal.

    As maneiras de minimizar o número de exceções são:

    Compartilhar as exceções entre as abstrações, utilizando um pequeno número de categorias de exceção.

    Emita as exceções especializadas derivadas das exceções padrão, mas manipule exceções mais generalizadas.

    Inclua os estados "exceptional" nos objetos e forneça os primitivos para verificar explicitamente a validade dos objetos.

    Ícone de Ok com a MãoDeclare todas as exceções emitidas

    As exceções originadas das funções (que não transmitem apenas as exceções) devem declarar todas as exceções lançadas em suas especificações de exceção: elas não devem gerar silenciosamente as exceções sem avisar seus clientes.

    ícone de dicaRelate as exceções na primeira ocorrência

    Durante o desenvolvimento, relate as exceções pelo mecanismo de log apropriado assim que possível, inclusive em "throw-point".

    Ícone de Ok com a MãoDefina as rotinas de tratamento de exceção na ordem de classe mais derivada para a de maior base

    As rotinas de tratamento de exceção devem ser definidas na ordem de classe mais mais derivada para a de maior base para evitar a codificação da rotina de tratamento inacessível; consulte o exemplo how-not-to-it, abaixo. Isso também assegurar que a rotina de tratamento mais apropriada captura a exceção porque as rotinas de tratamento são correspondidas em uma ordem de declaração.

    Exemplo

    Não execute:

    class base { ... };
    class derived : public base { ... };
    ...
    try
    {
      ...
    throw derived(...);
      //
    // Emita uma exceção de classe derivada
    }
    catch (base& a_base_failure)
      //
    // Mas a rotina de tratamento de classe base "catches", porque
    // ela combina primeiro!
    {
      ...
    }
    catch (derived& a_derived_failure) 
    //
    // Essa rotina de tratamento é inacessível!
    { 
      ...
    }      

    Ícone de Ok com a MãoEvite as rotinas de tratamento de exceção catch-all

    Evite as rotinas de tratamento de exceção catch-all (declarações de rotina de tratamento que utilizam ...), a menos que a exceção seja emitida novamente.

    As rotinas de tratamento catch-all devem ser utilizadas apenas para administração interna local; em seguida, a exceção deve ser emitida novamente para impedir o mascaramento do fato de que a exceção não pode ser manipulada nesse nível:

    try
    {
      ...
    }
    catch (...)
    { 
    if (io.is_open(local_file))
      {
    io.close(local_file);
      }
    throw;
    }      

    Ícone de Ok com a MãoCertifique-se de que os códigos de status da função possuam um valor apropriado

    Ao retornar os códigos de status como um parâmetro de função, sempre designe um valor para o parâmetro como a primeira instrução executável no corpo da função. Sistematicamente, torne todos os status um êxito por padrão ou uma falha por padrão. Considere todas as saídas possíveis da função, incluindo as rotinas de tratamento de exceção.

    Ícone de Ok com a MãoExecute as verificações de segurança localmente; não espere que seu cliente faça isso

    Se uma função puder produzir uma saída errônea, a menos que seja fornecida uma entrada apropriada, instale o código na função para detectar e relatar a entrada inválida de uma maneira controlada. Não confie em um comentário que informe ao cliente para transmitir os valores apropriados. É virtualmente garantido que, mais cedo ou mais tarde, esse comentário será ignorado, resultando em erros de difícil depuração, se os parâmetros inválidos não forem detectados.


    Capítulo 8

    Portabilidade

    Esse capítulo trata dos recursos de idioma que são uma priori não portáteis.

    Nomes de Caminho

    Ícone de Dedo IndicadorNunca utilize os nomes de caminho de arquivo de hardcode atribuído

    Os nomes de caminho não são representados de uma maneira padrão nos sistemas operacionais. Utilizá-los introduzirá as dependências de plataforma.

    Exemplo
    #include "somePath/filename.hh" // Unix
    #include "somePath\filename.hh" // MSDOS      

    Representação de Dados

    A representação e o alinhamento dos tipos são altamente dependentes da arquitetura da máquina. As premissas feitas sobre a representação e o alinhamento podem conduzir a surpresas desagradáveis e portabilidade reduzida.

    Ícone de Dedo IndicadorNão assuma a representação de um tipo

    Em específico, nunca tente armazenar um ponteiro em um int, um tipo numérico extenso ou qualquer outro tipo numérico - isso é altamente não-portátil.

    Ícone de Dedo IndicadorNão assuma o alinhamento de um tipo

    Ícone de Dedo IndicadorNão dependa de um determinado comportamento de limite baixo ou estouro

    Ícone de Ok com a MãoUtilize constantes "stretchable" sempre que possível

    As constantes flexíveis evitam problemas com as variações dos tamanhos das palavras.

    Exemplo
    const int all_ones = ~0;
    const int last_3_bits = ~0x7;     

    Conversões de Tipo

    Ícone de Dedo IndicadorNão converta de um tipo "mais curto" para um tipo "mais extenso"

    As arquiteturas da máquina podem ditar o alinhamento de determinados tipos. Converter de tipos com requisitos de alinhamento mais relaxados para tipos com requisitos de alinhamento mais severos pode conduzir a falhas no programa.


    Capítulo 9

    Reutilização

    Esse capítulo fornece orientação sobre a reutilização do código C++.

    Ícone de Ok com a MãoUtilize os componentes da biblioteca padrão sempre que possível

    Se as bibliotecas padrão não estiverem disponíveis, crie as classes com base nas interfaces de biblioteca padrão: isso facilitará a futura migração.

    Ícone de Ok com a MãoUtilize os gabaritos para reutilizar o comportamento independente de dados

    Utilize os gabaritos para reutilizar o comportamento, quando ele não for dependente de um tipo de dados específico.

    Ícone de Ok com a MãoUtilize a herança pública para reutilizar as interfaces de classe (subtipo)

    Utilize a herança public para expressar o relacionamento "isa" e reutilizar as interfaces de classe base e, opcionalmente, suas implementações.

    Ícone de Ok com a MãoUtilize a detenção em vez da herança privada para reutilizar as implementações de classe

    Evite a herança privada ao reutilizar a implementação ou modelar os relacionamentos "parciais/inteiros". A reutilização da implementação sem a redefinição é melhor alcançada pela detenção em vez da herança privada.

    Utilize a herança privada quando for necessária a redefinição de operações de classe-base.

    Ícone de Ok com a MãoUtilize a herança múltipla criteriosamente

    A herança múltipla deve ser utilizada criteriosamente porque contém muita complexidade adicional. [Meyers, 1992] fornece uma discussão detalhada sobre as complexidades atribuídas a ambigüidades de nome em potencial e herança repetida. As complexidades surgem de:

    Ambigüidades, quando os mesmos nomes forem utilizados por várias classes, qualquer referência não qualificada aos nomes será hereditariamente ambígua. A ambigüidade pode ser resolvida qualificando os nomes do membro com seus nomes de classe. No entanto, isso tem o infeliz efeito de destruir o polimorfismo e transformar as funções virtuais em funções estaticamente vinculadas.

    A herança repetida (herança de uma classe-base várias vezes por uma classe derivada através de caminhos diferentes na hierarquia de herança) de vários conjuntos de membros de dados da mesma base gera o problema de quais dos vários conjuntos de membros de dados devem ser utilizados?

    Os membros de dados herdados multiplicados podem ser evitados, utilizando a herança virtual (herança das classes-base virtuais). Então porque não utilizar sempre a herança virtual? A herança virtual possui o efeito negativo de alterar a representação de objeto subjacente e reduzir a eficiência de acesso.

    Representar uma política que necessite que toda a herança seja virtual, impondo, ao mesmo tempo, todo o espaço ao redor e penalidade de tempo, seria muito autoritário.

    Portanto, várias heranças requerem que os designers da classe estejam preparados para os futuros usos de suas classes: para conseguir tomar a decisão para utilizar a herança virtual ou não-virtual.


    Capítulo 10

    Problemas de Compilação

    Esse capítulo fornece orientação sobre os problemas de compilação

    Ícone de Ok com a MãoMinimize as dependências de compilação

    Não inclua em uma especificação de módulo outros arquivos de cabeçalho que sejam requeridos apenas pela implementação do módulo.

    Evite incluir os arquivos de cabeçalho em uma especificação para o propósito de obter a visibilidade a outras classes, quando for requerida apenas a visibilidade do ponteiro ou da referência; em vez disso, utilize as declarações de avanço.

    Exemplo
    // Especificação do módulo A, contida no arquivo "A.hh"
    #include "B.hh" // Don't include when only required by
                    // the implementation.
    #include "C.hh" // Don't include when only required by
                    // reference; use a forward declaration instead.
    class C;
    class A
    {
    C* a_c_by_reference; // Has-a by reference.
    };
    // Fim de "A.hh"      
    Notas

    Minimizar as dependências de compilação é racional para determinados idiomas de design, diversamente nomeados: Handle ou Envelope [Meyers, 1992] ou classes Bridge [Gamma]. Ao dividir a responsabilidade para uma abstração de classe entre duas classes associadas, uma fornecendo a interface de classe e a outra a implementação; as dependências entre uma classe e seus clientes são minimizadas uma vez que qualquer alteração na implementação (a classe de implementação) não provoca mais a recompilação dos clientes.

    Exemplo
    // Especificação do módulo A, contida no arquivo "A.hh"
    class A_implementation;
    class A
    {
    A_implementation* the_implementation;
    };
    // Fim de "A.hh"      

    Essa abordagem também permite que a classe da interface e a classe de implementação sejam especializadas como duas hierarquias de classe separadas.

    Ícone de Dedo IndicadorDefina o símbolo NDEBUG com um valor específico

    O símbolo NDEBUG foi utilizado tradicionalmente para compilar o código de asserção implementado utilizando a macro de asserção. O paradigma de uso tradicional era definir o símbolo quando fosse desejado eliminar as asserções; no entanto, os desenvolvedores geralmente desconheciam a presença das asserções e, portanto, nunca definiam o símbolo.

    Defendemos o uso da versão de gabarito da asserção; nesse caso, o símbolo NDEBUG deve receber um valor explícito: 0, se o código de asserção for desejado; não-zero para eliminação. Qualquer código de asserção subseqüentemente compilado sem fornecer ao símbolo NDEBUG um valor específico gerará erros de compilação; portanto, trazendo a atenção dos desenvolvedores à existência do código de asserção.


    Resumo da Orientação

    Segue um resumo de todas as diretrizes apresentadas nesse folheto.

    Ícone de Dedo IndicadorRequisitos ou Restrições

    Utilize o Senso Comum
    Sempre utilize #include para obter acesso a uma especificação de módulo
    Nunca declare nomes que comecem com um ou mais sublinhados ('_')
    Limite as declarações globais apenas aos espaços de nomes
    Sempre forneça um construtor padrão para as classes com construtores explicitamente declarados
    Sempre declare os construtores de cópia e os operadores de designação para as classes com os membros de dados do tipo de ponteiro
    Nunca declare novamente os parâmetros do construtor para ter um valor padrão
    Sempre declare os destruidores como virtuais
    Nunca redefina as funções não virtuais
    Nunca chame as funções de membro a partir de um inicializador construtor
    Nunca retorne uma referência a um objeto local
    Nunca retorne um ponteiro de-referenciado inicializado com new
    Nunca retorne uma referência non-const ou ponteiro para os dados do membro
    Faça com que operator= retorne uma referência a *this
    Faça com que operator= verifique a auto-designação
    Nunca lance "constness" de um objeto constante
    Não assuma qualquer ordem de avaliação de expressão específica
    Não utilize o lançamento de estilo antigo
    Utilize o novo tipo bool para as expressões Booleanas
    Nunca compare diretamente com o valor Booleano verdadeiro
    Nunca compare os ponteiros aos objetos que não estão dentro da mesma matriz
    Sempre designe um valor de ponteiro null a um ponteiro de objeto excluído
    Sempre forneça uma ramificação padrão para switch-statements para capturar os erros
    Não utilize goto-statement
    Evite misturar as operações de memória C e C++
    Sempre utilize delete[] ao excluir os objetos de matriz criados por new
    Nunca utilize os nomes de caminho de arquivo de hardcode atribuído
    Não assuma a representação de um tipo
    Não assuma o alinhamento de um tipo
    Não dependa de um determinado comportamento de limite baixo ou estouro
    Não converta de um tipo "mais curto" para um "mais extenso"
    Defina o símbolo NDEBUG com um valor específico

    Ícone de Ok com a MãoRecomendações

    Coloque as especificações de módulo e as implementações em arquivos separados
    Selecione um único conjunto de extensões de nome de arquivo para distinguir os cabeçalhos dos arquivos de implementação
    Evite definir mais de uma classe por especificação do módulo
    Evite colocar as declarações de implementação privada nas especificações do módulo
    Coloque as definições de função seqüencial do módulo em um arquivo separado
    Quebre grandes módulos em várias unidades de conversão, se o tamanho do programa for uma preocupação
    Isole as dependências de plataforma
    Proteja-se das inclusões repetidas de arquivo
    Utilize um símbolo de compilação condicional "No_Inline" para contestar a compilação seqüencial
    Utilize um estilo de recuo pequeno consistente para instruções aninhadas
    Recue os parâmetros de função a partir do nome da função ou nome do escopo
    Utilize um comprimento máximo de linha que caberia no tamanho do papel impresso padrão
    Utilize a dobra de linha consistente
    Utilize os comentários de estilo C++ em vez de comentários de estilo C
    Maximize a proximidade do comentário para o código fonte
    Evite os comentários de fim de linha
    Evite os cabeçalhos de comentário
    Utilize uma linha de comentário vazia para separar os parágrafos de comentário
    Evite a redundância
    Escreva o código de auto-documentação em vez dos comentários
    Documente as classes e as funções
    Escolha uma convenção de nomenclatura e aplique-a consistentemente
    Evite utilizar nomes de tipo que sejam diferentes apenas por tipo de letra
    Evite o uso de abreviações
    Evite o uso de sufixos para denotar as construções de idioma
    Escolha nomes limpos, legíveis e significativos
    Utilize a ortografia correta nos nomes
    Utilize as cláusulas de predicado positivo para Booleanos
    Utilize os espaços de nome para particionar os nomes globais em potencial por subsistemas ou por bibliotecas
    Utilize substantivos ou frases substantivas para os nomes de classe
    Utilize os verbos para nomes de função de tipo de procedimento
    Utilize a sobrecarga de função quando for pretendido o mesmo significado geral
    Aumente os nomes dos elementos gramaticais para enfatizar o significado
    Escolha nomes de exceção com um significado negativo
    Utilize os adjetivos definidos pelo projeto para os nomes de exceção
    Utilize primeiras letras maiúsculas para o exponente de ponto flutuante e dígitos hexadecimais.
    Utilize um espaço de nomes para agrupar a funcionalidade não-classe
    Minimize o uso dos dados de escopo global e do espaço de nomes
    Utilize a classe em vez de struct para implementar os tipos de dados abstratos
    Declare os membros de classe na ordem de acessibilidade decrescente
    Evite declarar membros de dados públicos ou protegidos para tipos de dados abstratos
    Utilize os atalhos para preservar o encapsulamento
    Evite fornecer definições de função nas declarações da classe
    Evite declarar vários operadores de conversão e construtores únicos de parâmetro
    Utilize as funções não-virtuais criteriosamente
    Utilize os inicializadores construtores em vez das designações nos construtores
    Cuidado ao chamar as funções de membro nos construtores e destruidores
    Utilize static const para constantes de classe integral
    Sempre declare um tipo de retorno de função explícita
    Sempre forneça nomes de parâmetros formais nas declarações de função
    Esforce-se para funções com um único ponto de retorno
    Evite criar função com efeitos colaterais globais
    Declare os parâmetros de função na ordem de volatilidade e importância decrescente
    Evite declarar funções com um número variável de parâmetros
    Evite declarar novamente as funções com parâmetros padrão
    Maximize o uso de constantes nas declarações de função
    Evite transmitir os objetos por valor
    Utilize as funções seqüenciais em preferência a #define para a expansão da macro
    Utilize os parâmetros padrão em vez da sobrecarga de função
    Utilize a sobrecarga de função para expressar as semânticas comuns
    Evite sobrecarregar as funções que possuem ponteiros e inteiros
    Minimize a complexidade
    Evite o uso de tipos fundamentais
    Evite utilizar valores literais
    Evite utilizar a diretiva #define do pré-processador para definir as constantes
    Declare os objetos próximos a seus pontos de primeiro uso
    Sempre inicialize os objetos const na declaração
    Inicialize os objetos na definição
    Utilize uma if-statement ao ramificar expressões Booleanas
    Utilize uma switch-statement ao ramificar valores distintos
    Utilize um for-statement ou while-statement quando um teste pré-iteração for requerido em um loop
    Utilize um do-while-statement quando um teste pós-iteração for requerido em um loop
    Evite o uso das instruções jump nos loops
    Evite o ocultamento dos identificadores nos escopos aninhados
    Utilize as asserções de forma liberal durante o desenvolvimento para detectar os erros
    Utilize as exceções apenas para condições realmente excepcionais
    Derive as exceções de projeto a partir das exceções padrão
    Minimize o número de exceções utilizadas por uma determinada abstração
    Declare todas as exceções emitidas
    Defina as rotinas de tratamento de exceção na ordem de classe mais derivada para a de maior base
    Evite as rotinas de tratamento de exceção catch-all
    Certifique-se de que os códigos de status da função possuam um valor apropriado
    Execute as verificações de segurança localmente; não espere que seu cliente faça isso
    Utilize as constantes "stretchable" sempre que possível
    Utilize os componentes da biblioteca padrão sempre que possível

    ícone de dicaDicas

    Defina os tipos de sistema globais na amplitude do projeto
    Utilize typedef para criar sinônimos para reforçar o significado local
    Utilize os parênteses redundantes para esclarecer as expressões compostas
    Evite aninhar as expressões muito profundamente
    Utilize 0 para ponteiros nulos em vez de NULL
    Relate as exceções na primeira ocorrência


    Bibliografia

    [Cargill, 92] Cargill, Tom. 1992. C++ Programming Styles Addison-Wesley.

    [Coplien, 92] Coplien, James O. 1992. Advanced C++ Programming Styles and Idioms, Addison-Wesley.

    [Ellemtel, 93] Ellemtel Telecommunications Systems Laboratories. June 1993. Programming in C++ Rules and Recommendations.

    [Ellis, 90] Ellis, Margaret A. and Stroustrup, Bjarne.1990. The Annotated C++ Reference Manual, Addison-Wesley.

    [Kruchten, 94] Kruchten, P. May 1994. Ada Programming Guidelines for the Canadian Automated Air Traffic System.

    [Lippman, 96] Lippman, Stanley, B. 1996. Inside the C++ Object Model, Addison-Wesley.

    [Meyers, 92] Meyers, Scott. 1992. Effective C++, Addison-Wesley.

    [Meyers, 96] Meyers, Scott. 1996. More Effective C++, Addison-Wesley.

    [Plauger, 95] Plauger, P.J. 1995. The Draft Standard C++ Library, Prentice Hall, Inc.

    [Plum, 91] Plum, Thomas and Saks, Dan. 1991. C++ Programming Guidelines, Plum Hall Inc.

    [Stroustrup, 94] Stroustrup, Bjarne. 1994. The Design and Evolution of C++, Addison-Wesley.

    [X3J16, 95] X3J16/95-0087 | WG21/N0687. April 1995. Working Paper for Draft Proposed International Standard for Information Systems-Programming Language C++.