Programming 版 (精华区)

发信人: netiscpu (说不如做), 信区: Programming
标  题: C++ 编程准则与忠告 之 Portable Code
发信站: 紫 丁 香 (Sun Jul 26 11:25:43 1998), 转信




18 Portable Code

Back to index 

18.1 Data Abstraction

Back to index 

Port. Rec. 1 Avoid the direct use of pre-defined data types in
declarations. 

An excellent way of transforming your world to a "vale of tears" is to
directly use the pre-defined data types in declarations. If it is
later necessary, due to portability problems, to change the return
type of a function, it may be necessary to make change at a large
number of places in the code. One way to avoid this is to declare a
new type name using classes or typedefs to represent the types of
variables used. In this way, changes can be more easily made. This may
be used to give data a physical unit, such as kilogram or meter. Such
code is more easily reviewed. (For example, when the code is
functioning poorly, it may be noticed that a variable representing
meters has been assigned to a variable representing kilograms). It
should be noted that a typedef does not create a new type, only an
alternative name for a type. This means that if you have declared
typedef int Error, a variable of the type Error may be used anywhere
that an int may be used. 

See also chapter 12, Rec. 49! 


Example 67 Type declarations using 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;

        


18.2 Sizes of Types

Back to index 

Port. Rec. 2 Do not assume that an int and a long have the same size. 
Port. Rec. 3 Do not assume that an int is 32 bits long (it may be only
16 bits long). 

Port. Rec. 4 Do not assume that a char is signed or unsigned. 
Port. Rec. 5 Always set char to unsigned if 8-bit ASCII is used. 

In the definition of the C++ language, it has not yet been decided if
a char is signed or unsigned. This decision has instead been left to
each compiler manufacturer. If this is forgotten and this
characteristic is exploited in one way or another, some difficult bugs
may appear in the program when another compiler is used. 

If 8-bit ASCII is used (as is quite likely in the future) and
comparisons are made of two characters, it is important that unsigned
char is used. 


18.3 Type Conversions

Back to index 

Port. Rec. 6 Be careful not to make type conversions from a "shorter"
type to a "longer" one. 

Port. Rec. 7 Do not assume that pointers and integers have the same
size. 
Port. Rec. 8 Use explicit type conversions for arithmetic using signed
and unsigned values. 


A processor architecture often forbids data of a given size to be
allocated at an arbitrary address. For example, a word must begin on
an "even" address for MC680x0. If there is a pointer to a char which
is located at an "odd" address, a type conversion from this char
pointer to an int pointer will cause the program to crash when the int
pointer is used, since this violates the processor's rules for
alignment of data. 


18.4 Data Representation

Back to index 

Port. Rec. 9 Do not assume that you know how an instance of a data
type is represented in memory. 

Port. Rec. 10 Do not assume that longs, floats, doubles or long
doubles may begin at arbitrary addresses. 


The representation of data types in memory is highly
machine-dependent. By allocating data members to certain addresses, a
processor may execute code more efficiently. Because of this, the data
structure that represents a class will sometime include holes and be
stored differently in different process architectures. Code which
depends on a specific representation is, of course, not portable. 

See18.3 for explanation of Port. Rec. 10. 

18.5 Underflow/Overflow

Back to index 

Port. Rec. 11 Do not depend on underflow or overflow functioning in
any special way. 

18.6 Order of Execution

Back to index 

Port. Rec. 12 Do not assume that the operands in an expression are
evaluated in a definite order. 

Port. Rec. 13 Do not assume that you know how the invocation mechanism
for a function is implemented. 

Port. Rec. 14 Do not assume that an object is initialized in any
special order in constructors. 

Port. Rec. 15 Do not assume that static objects are initialized in any
special order. 


If a value is modified twice in the same expression, the result of the
expression is undefined except when the order of evaluation is
guaranteed for the operators that are used. 

The order of initialization for static objects may present problems. A
static object may not be used in a constructor, if it is not
initialized until after the constructor is run. At present, the order
of initialization for static objects, which are defined in different
compilation units, is not defined. This can lead to errors that are
difficult to locate (see Example 69). There are special techniques for
avoiding this. See Example 29! 



Example 68 Do not depend on the order of initialization in
constructors. 

  #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

  }

        



Example 69 Initialization of static objects 

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

        


18.7 Temporary Objects

Back to index 

Port. Rec. 16 Do not write code which is dependent on the lifetime of
a temporary object. 


Temporary objects are often created in C++, such as when functions
return a value. Difficult errors may arise when there are pointers in
temporary objects. Since the language does not define the life
expectancy of temporary objects, it is never certain that pointers to
them are valid when they are used. 

One way of avoiding this problem is to make sure that temporary
objects are not created. This method, however, is limited by the
expressive power of the language and is not generally recommended. 

The C++ standard may someday provide an solution to this problem. In
any case, it is a subject for lively discussions in the
standardization committee. 



Example 70 Difficult error in a string class which lacks output
operator 

  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 << a + b;

        


18.8 Pointer Arithmetic

Back to index 

Port. Rec. 17 Avoid using shift operations instead of arithmetic
operations. 
Port. Rec. 18 Avoid pointer arithmetic. 

Pointer arithmetic can be portable. The operators == and != are
defined for all pointers of the same type, while the use of the
operators <, >, <=, >= are portable only if they are used between
pointers which point into the same array. 

--

                              Enjoy Linux!
                          -----It's FREE!-----

※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: mtlab.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:210.034毫秒