Getting Started Documentation Glish Learn More Programming Contact Us
Version 1.9 Build 1556
News FAQ
Search Home


next up previous
Up: AIPS++ Programming Standards DRAFT Version Previous: Guidelines

Subsections


Warnings

Machine Size and Ordering Dependencies

Size of Builtin Types

Concerning the sizes of the standard integral types the following are guaranteed 9: Additionally, the following are the minimum sizes guaranteed for the integral types 10 and the floating point types 11: The size of an int is typically 16 to 32 bits, but this size is not guaranteed (32 bits in all probability for machines running AIPS++). The size of the integral and floating point types are available in the header files <limits.h> and <float.h> respectively. (see p.72 Plum)

In addition, dependencies on the byte ordering of numeric types should be avoided. For example, the following is a bad idea:

short i = 25;
char *cptr;

cptr = (char *) \&i;
One cannot anticipate the byte to which cptr points, given the possible difference in architecture byte orderings (p.74 Plum). In addition, when passing integral values between machines, the values should be converted to/from network byte ordering via the routines in <netinet/in.h>, e.g. htonl, etc.

Structure Size and Member Offset

Another possible source of alignment errors, is hard-coding offsets into structures because the packing of structure members into the storage is implementation dependent. The proper way to determine the offset is to use the offsetof macro in <stddef.h>(see p. 84 Plum). So for a struct like:
struct mystruct \{
  int x;
  int y;
\};
offsetof(mystruct, y) might yield 4.

Character Constants and String Literals

Character constants should contain only one character because differences in machine byte order may lead to values which differ in numeric value of character sequence, for example:
short crlf = '\r\n';
is a non-portable use of character constants. A literal string can be used if appropriate, or a left shift can be used to portably generate a integral value out of characters, e.g.
\#define CHAR2(a, b) (((a) << CHAR\_BIT) + (b))
portably combines two characters 12.

String literals could also be a source of problems. They should not be modified instead named arrays should be used. In the following example, the first method is the portable way to obtain a modifiable string; the second is the wrong way 13:

1.
static char fname[] = "/tmp/edXXXXXX"; mktemp(fname);
2.
mktemp("/tmp/edXXXXXX")

Creation and Deletion

Initialization

It is important to remember that global static objects which have a constructor or are initialized with a constant expression are initialized in the order in which they are encountered 14. There are ways to ensure a certain initialization order 15.

Another possible source of problems is with the initialization of member variables in a constructor definition. The member variables are initialized in the order in which they occur in the class, regardless of their order in the constructor definition. So for example:

class A\{
  private:
    int x;
    int y;
    int z;
  public:
    A() : z(12), y(9), x(z+y) \{\};
    A(int t) : x(8), y(t), z(0) \{\};
\};
will cause problems, because its order of initialization will always be -- x, y, then z. This choice was made to have consistent initialization orders. Otherwise, if the initialization order was dependent upon the order in the constructor definition, the two constructors in this example would have different initialization orders 16.

In general, it is best to avoid designs which depend on the initialization order of static globals or static members. Also, it is important to remember the initialization order of member variables to avoid problems.

Deletion of Arrays

When deleting an array of objects, it is important to call the delete operation with the array specifier. Otherwise, the destructors for each of the elements in the array will not be called. Only the destructor for the first one will be called. So for example, if we have a string class which allocates memory to hold the string:
class String\{
  private:
    char *rep;
  public:
    String(char *);
    String()\{ rep = 0;\}
    const char *operator*()\{ return rep;\}
    String \&operator=(char *);
    ~String()\{ delete rep;\}
\};

String::String(char *v)\{
  if (v != 0)\{
    rep = new char[strlen(v)+1];
    strcpy(rep,v);
  \} else
    rep = 0;
\}

String \&String::operator=(char *newval)\{
  if (rep != 0)
    delete rep;
  rep = new char[strlen(newval)+1];
  strcpy(rep,newval);
  return(*this);
\}

main() \{
  String *s = new String[3];

    s[0] = "Hello";
    s[1] = "there";
    s[2] = "friend";

    delete s;
\}
the call to delete s only causes the destructor for the first s[0] to be called. The space allocated for there and friend is lost forever. The correct way to delete s is delete [] s 17.

Another interesting point is that for an array of objects to be allocated via new a default constructor has to be defined, i.e. a constructor without parameters 18. Specifying a constructor with all default parameters is not sufficient.

Virtual Destructors

Sometimes it is important for destructors to be declared as virtual in a base class. This is important when all of the following hold 19: It is important for the destructor to be virtual when these conditions hold because when deleting a derived object via a pointer to the base class, only the destructor for the base class will be called. The derived class' destructor will not be called, thus possibly causing memory leaks.

References

Another cause of memory problems is references. It is generally a bad idea to initialize a reference to memory which can be deleted, e.g. free store, automatic variables. Typically this problem will result in segmentation violations or the like, and not in memory leaks. However, a substantial amount of time could be expended tracking down the source of error (see p.38 Plum).

New and Delete

Some problems can arise with classes which define the operators new and delete with more than one argument because these definitions hide the global or base class definitions. For example 20:
typedef void (*PEHF)();

class X \{
  public:
    void *operator new(size\_t, PEHF pehf);
\}

main()\{
  void specialErrorHandler();

  X *px1 = new (specialErrorHandler) X; 
  X *px2 = new X;
\}
The first new succeeds with no problems. The second however causes an error because the global new which takes one parameter, size_t, is hidden by the local new in class X. This problem can be corrected by either calling the global new explicitly, X *px2 = ::new X, or by providing a new operator in X which takes only one parameter. It is also a good idea to supply both new and delete if one is needed to avoid future maintenance problems.

Copy Operator

Be aware that if you don't explicitly disallow the copy operator, Type::operator=(Type&), one will be supplied for you, i.e. a bit copy. One way to ensure that there is no copy operator is to make its declaration private, preventing users from accessing it, and not providing a definition, preventing friends from using it.


next up previous
Up: AIPS++ Programming Standards DRAFT Version Previous: Guidelines
Please send questions or comments about AIPS++ to aips2-request@nrao.edu.
Copyright © 1995-2000 Associated Universities Inc., Washington, D.C.

Return to AIPS++ Home Page
2006-10-15