Regras e recomendações
18 - Código portável

  • 18.1 - Abstração de dados
  • 18.2 - Tamanhos dos tipos de dados
  • 18.3 - Conversão de tipos
  • 18.4 - Representação dos dados
  • 18.5 - Underflow/overflow
  • 18.6 - Ordem de execução
  • 18.7 - Objetos temporários
  • 18.8 - Aritmética de pointers

18.1 - Abstração de dados

Portabilidade. Recomendações:
  • 1: Evite o uso direto de tipos de dados pré-definidos nas declarações

Uma excelente maneira de transformar o seu mundo em um vale de lágrimas é usar diretamente tipos de dados pré-definidos nas declarações. Se mais tarde, devido a problemas de portabilidade, for preciso alterar o tipo de retorno de uma função, poderá ser necessário fazer alterações em um grande número de lugares no código. Uma maneira de evitar isso é declarar um novo tipo usando classes ou typedef para representar os tipos de variáveis usadas. Dessa maneira, alterações futuras se tornarão mais fáceis. Isso pode ser usado para atribuir aos dados uma unidade física, como kilograma ou metro. Um código assim é mais facilmente revisavel.

Por exemplo, quando o código funcionar mal, pode-se perceber mais facilmente que uma variável representando metros foi atribuída a uma outra variável que representa kilogramas. Perceba que typedef não cria um novo tipo, é apenas um nome alternativo para o tipo. Ou seja, se você declarar typedef int Error, uma variável tipo Error poderá ser usada em qualquer lugar onde um int puder ser usado.

Exemplo 67: Declarações de tipo usando typedef

  // Instead of:
  long int time;
  short int mouseX;
  char* menuName;
        
  // Use (for example):
  typedef long int TimeStamp;
  typedef short int Coordinate;
  class String { /* ... */ };
        
  // and:
  TimeStamp time;
  Coordinate mouseX;
  String menuName;
Topo
18.2 - Tamanhos dos tipos de dados

Portabilidade. Recomendações:
  • 2: Não assuma que um int e um long tem o mesmo tamanho
  • 3: Não assuma que um int tem 32 bits de tamanho, porque pode ter apenas 16 bits de tamanho)
  • 4: Não assuma que char é signed ou unsigned
  • 5: Sempre determine unsigned char se estiver usando ASCII 8-bits

Na definição da linguagem C++, ainda não está decidido se um char é signed ou unsigned. Essa decisão tem sido deixada para os fornecedores de compiladores. Se você se esquecer disso, alguns bugs de difícil localização poderão aparecer no programa quando um outro compilador for utilizado.

Quando usando ASCII 8-bits, é importante usar unsigned char para poder fazer comparações entre dois caracteres .

Topo
18.3 - Conversão de tipos

Portabilidade. Recomendações:
  • 6: Tenha cuidado para não fazer conversões de tipo de um shorter para um longer
  • 7: Não assuma que pointers e inteiros tem o mesmo tamanho
  • 8: Use conversão explícita de tipos para valores aritiméticos signed ou unsigned

A arquitetura dos processadores normalmente proíbem que dados de um determinado tamanho sejam alocados a um endereço de memória qualquer. Por exemplo, uma palavra deve começar em um endereço par para o processador MC680x0. Se um pointer para um char for alocado em um endereço impar, uma conversão desse pointer char para um pointer int vai causar uma queda do programa quando o pointer int for usado, porque isso viola uma regra do processador para alinhamento de dados.

Topo
18.4 - Representação dos dados

Portabilidade. Recomendações:
  • 9: Não assuma que você sabe como uma instância de um tipo de dados é representada na memória
  • 10: Não assuma que longs, floats, doubles ou long doubles podem começar em um endereço de memória qualquer.

A representação de tipos de dados na memória é altamente dependente da máquina. O processador pode executar o código mais eficientemente alocando os dados em endereços com certas características. Por isso, a estrutura de dados que representa uma classe poderá, algumas vezes, incluir espaços vagos, e ser armazenada diferentemente em diferentes arquiteturas de processadores. Código que dependa de uma representação interna específica se torna então não portável.

Topo
18.5 - Underflow/overflow

Portabilidade. Recomendações:
  • 11: Não dependa, de nenhuma maneira, do funcionamento de underflow ou overflow
Topo
18.6 - Ordem de execução

Portabilidade. Recomendações:
  • 12: Não assuma que os operandos em uma expressão serão resolvidos em uma ordem definitiva.
  • 13: Não assuma que você sabe como são implementados os mecanismos de invocação de funções
  • 14: Não assuma que um objeto é inicializado no construtor em qualquer ordem especial
  • 15: Não assuma que objetos estáticos são incializados em qualquer ordem especial

Se um valor for modificado duas vezes na mesma expressão, o resultado da expressão pode ser indefinido, exceto se a ordem de resolução for garantida pelos operadores usados.

A ordem de inicialização dos objetos estáticos pode apresentar problemas. Um objeto estático não pode ser usado em um construtor se não estiver inicializado o momeno em que o construtor for executado.

Exemplo 68: Não dependa da ordem de inicialização em construtores

  #include 
  class X
  {
    public:
      X(int y);
    private:
      int i;
      int j;
  };
        
  inline X::X(int y) : j(y), i(j)    // No! j may not be initialized before i !!
  {
    cout <<"i:" << i << " & " << "j:" << j /lt< endl; } main() { X x(7); // Rather unexpected output: i:0 & j:7 } 
Exemplo 69: Inicialização de objetos estáticos
  // Foo.hh
        
  #include 
  #include 
        
  static unsigned int const Size = 1024;
        
  class Foo 
  {
    public:
      Foo( char* cp );     // Constructor
      // ...
    private:
      char buffer[Size];
      static unsigned counter;  // Number of constructed Foo:s
  };
        
  extern Foo foo_1;
  extern Foo foo_2;
        
  // Foo1.cc
  #include "Foo.hh"
        
  unsigned Foo::counter = 0;
  Foo foo_1 = "one";
        
  //Foo2.cc
  #include "Foo.hh"
        
  Foo foo_2 = "two";
        
  Foo::Foo( char* cp )    // Irrational constructor
  {
    strncpy( buffer, cp, sizeof(buffer) );
    foos[counter] = this;
    switch ( counter++ )
    {
      case 0:
      case 1:    
        cout <<::foo_1.buffer << "," << ::foo_2.buffer << endl; break; default: cout << "Hello, world" << endl; } } // If a program using Foo.hh is linked with Foo1.o and Foo2.o, either // ,two or one, is written on standard output depending on // one,two one,two the order of the files given to the linker. 
Topo
18.7 - Objetos temporários

Portabilidade. Recomendações:
  • 16: Não escreva código dependente do tempo de vida de um objeto temporário

Objetos temporários são criados com freqüência em C++, como quando uma função retorna um valor. Podem surgir erros difíceis de se localizar quando há pointers em objetos temporários. Já que a linguagem não define a expectativa de vida de objetos temporários, nunca se sabe com certeza se esses pointers ainda são válidos no momento em que são usados.

Uma maneira de evitar esse problema é assegurar-se de que esses objetos temporários não sejam criados. Esse método, entretanto, é limitado pelo expressivo poder da linguagem é geralmente não é recomendável.

Exemplo 70: Erro difícil em uma classe string que carece um operador de saída

  class String
  {
    public:
      operator const char*() const;    // Conversion operator to const char*
      friend String operator+( const String& left, const String& right );
      // ...
  };
        
  String a = "This may go to ";
  String b = "h***!";
    // The addition of a and b generates a new temporary String object.
    // After it is converted to a char* by the conversion operator, it is
    // no longer needed and may be deallocated. This means that characters
    // which are already deallocated are printed to cout -> DANGEROUS!!
  cout <
Topo
18.8 - Aritmética de pointers

Portabilidade. Recomendações:
  • 17: Evite usar operações de shift em lugar de operações aritméticas
  • 18: Evite operações aritméticas com pointers

Operações aritméticas com pointers podem ser portáveis. Os operadores == e != são definidos para todos os pointers do mesmo tipo, os operadores <, >, <=, => são portáveis apenas quando usados entre pointers que apontam para a mesma matriz.

Topo Índice

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |