forked from casacore/casacore-notes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path140.texi
679 lines (606 loc) · 27.2 KB
/
140.texi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename aips-standards.info
@settitle AIPS++ Programming Standards
@c %**end of header
@ifinfo
This document discusses the C++ coding standards and suggestions for the
@sc{aips++} coding effort. This document is intended to be minimalistic because
several lengthy expositions of C++ etiquette exist in other places.
@footnote{Plum, Saks, ``C++ Programming Guidelines'', Plum Hall, 1991.}
Here we wish only to outline basic standards and the most important
suggestions.
@end ifinfo
@titlepage
@title AIPS++ Programming Standards
@subtitle DRAFT Version
@vskip 0pt plus 1fill
@author Darrell Schiebel
Last modified: $Date$
By: $Author$
@c The above line is maintained by RCS - do not modify it.
@c NOTE #140
@end titlepage
@node Top, Overview, (dir), (dir)
@chapter Introduction
In this document, will the discuss the more basic standards and suggestions
for @strong{C++} development. The conventions of Plum and Saks
will be used for terminology @footnote{Plum, Saks, C++ Programming Guidelines,
Plum Hall, 1991}; a standard is a rule which is nearly always followed,
and a guideline is a recommendation. Wherever possible, a coding example will be provided
to clarify the current discussion, and a justification will be given for all
standards and guidelines.
This is intended as a living document, and as such all of its contents are
open to discussion and debate. Additions can be
added if it can be successfully argued that they will help the maintainability,
understandability, or reusability of @sc{aips++} software, or sections can be
removed it can be argued that their benefits do not
significantly increase the quality of @sc{aips++} software. It is very important to
maintain a minimal set of standards to avoid a overwhelming barrage of
rules which could result in the standards not being followed.
There will be two documents which will accompany this document. One will describe
the format for source code documentation and the tool(s) for extracting the
documentation and creating @emph{texinfo} files or @emph{man} pages. Perhaps, at
some point, the coding standards and documentation standards will be combined, but
for now they will be separate. The second will describe the exception mechanisms
which will be used for signaling and handling errors.
There are several other sources of information utilized in creating this
document. Meyers book on effective @strong{C++} programming
proved useful @footnote{Meyers, Effective C++, Addison Wesley, 1992}, but
many of the points which this book makes is also in the ``C++ Programming Guidelines''.
However, the ``C++ Programming Guidelines'' is much more terse. The @sc{ARM}
@footnote{Stroustrup, Ellis, The Annotated C++ Reference Manual,
Addison Wesley, 1990} was also used. Since the @sc{ARM} is the @sc{ANSI} draft
standard for @strong{C++}, it is the @emph{last word} when questions arise, and
it also contains interesting commentary on language features.
@chapter Standards
@section @code{0} and @code{NULL}
The integer constant @code{0} should be used instead of the usual @code{NULL} from
@code{<stdio.h>} or @code{<string.h>}. In @strong{C}, this @code{#define} was used to insure
that pointer assignments had the proper size. In @strong{C++}, however, this can
result in type mismatch problems, e.g. @code{NULL} may be defined to be
@code{(void *)0}. Zero,``@code{0}'', holds a special place in the @strong{C++} type
system because even though zero's type is @code{int}, it is automatically cast to
any pointer, char, or float. (See p.30 Plum, p.87 Meyers, p.35 ARM)
@section Header Files
Every include file must have protecting @code{#if defined()}s
to prevent multiple inclusion, so for ``aips.h'' the include file would be
enclosed by:
@example
#if !defined(AIPS_H_)
#define AIPS_H_
BODY OF INCLUDE FILE
#endif
@end example
Header files should not initialize values. The user of a given header file should
be assured that including the header file will not increase the object-code
size. This practice forces the data to be owned by a given source file, and
avoids complicated initialization constructs in the header
files. (See p.154 Plum)
@section @code{const}, @code{#define}, and macros
Constants, @code{#define}s, and macros, should have the smallest scope possible;
restricted to a single source file when possible to avoid name clashes.
The @strong{C++} @emph{const}, @emph{inline}, and @emph{enum} facilities
should be used as opposed to defines, because these facilities provide a
typesafe means of defining constants, as opposed to the preprocessor which
defeats the type system, and can make macro bugs difficult to find.
Sometimes, however, @code{#define}s must be used, for example include file
protection against multiple includes.
Another example of preprocessor utility is the @code{##} operator for
concatenation of tokens. This has no equivalent operation in @strong{C++}.
In addition, @code{#ifdef}s also provide the only reasonable means of
conditional compilation. e.g.
@example
#if defined(__cplusplus)
C++ CODE
#elif defined(__STDC__)
ANSI C CODE
#else
K&R C CODE
#endif
@end example
With the exception of include file protection, preprocessor functions
should be used with caution. The @code{##} operator, for example, is not
available on K&R @strong{C} compilers. The typical work around is
to define a macro which uses an empty comment, @code{/**/}, for
concatenation. Careless @code{ifdef}s for conditional
compilation throughout code make it difficult to maintain.
When the preprocessor is utilized macro or defines which begin with a
double underscore, ``__'', or single underscore, ``_'', should be
avoided because are used by @strong{C} and @strong{C++}.
@section Memory Allocation
@code{new} and @code{delete} will be used for memory allocation and deletion
instead of the malloc/free family of functions because, for objects, new and
delete allow for destructors to clean up storage allocated by the object.
In addition, the use of new and delete allows redefinition the global
new and delete, @code{::new} and @code{::delete}. By redefining these
global operators, allocation/deallocation inconsistencies can be tracked
down to the source code line, with the help of the preprocessor. Thus new
and delete allow for finer control of the memory allocation process for
all dynamic data. (p.18 Meyers) However, this method is useful primarily for
testing, and no code should be checked into the system with @code{::new} or
@code{::delete} defined because defining these affects the whole project.
Modifications to @code{::new} or @code{::delete} must be approved by the
various member of the consortium.
@section Name Space Management
To avoid name space conflicts, global variables should be avoided. A better
approach is to use static member variables of a class.
To avoid type clashes we will use the method which was used in InterViews
@footnote{Linton, et. al., @emph{InterViews 3.0} Class Library}
A macro will be defined,
@example
#define _lib_aips(name) aips##name
@end example
@noindent
which will create a token unique to the aips project. This will be generated by
sed/egrep which will pull out class definitions and generate the defines
for the class name. So a define will be generated for each class name,
@code{#define MyClass _lib_aips(MyClass)}. Thus aips scope can be entered
by including a given file, @code{aips-enter.h}, and aips scope can be exited by
including a different file, @code{aips-exit.h}.
Thus to enter an @sc{aips++} code section the programmer would include the file
@code{aips-enter.h} and this file would be automatically generated from the
other @sc{aips++} header files and might look like:
@example
// No multiple inclusion protection to allow reentering AIPS++
// scope in the same file, file contains only #defines.
#define String _lib_aips(String)
...MORE SIMILAR DEFINES...
@end example
@noindent
Thus every time a developer referenced a member of class @code{String} the
class name would be replaced with @code{_lib_aips(String)} which would
expand as described above to @code{aips##String} and finally to
@code{aipsString}. Then to exit the scope of aips code the developer would
include file @code{aips-exit.h} which is also automatically generated. This
file might look like:
@example
#undef String
...MORE SIMILAR UNDEFS...
@end example
@noindent
Thus each reference to @code{String} would now reference a string defined
possibly in some other class library. Hopefully, this sort of context
switching will be the exception rather that the rule, and developers
will simply include @code{aips_enter.h} at the top of a source file
and not have to worry about name clashes.
In addition, the use of identifiers which begin with a double underscore,
``__'', or a single-underscore, ``_'', should be avoided. Identifiers
which begin with a double underscore are reserved for @strong{C++} and
those which begin with a single underscore are reserved for @strong{C}.
@section Gotos
The use of gotos in code should be avoided. This well used standard is
especially applicable in object oriented design. Gotos in code make the
code much more difficult to read and violate logical scoping.
(See Plum, p.126)
@section Switch Statements
Switch statements should be used when applicable. However, designs which
rely upon switch statements based on the @emph{runtime type} of objects
should be avoided. Occasionally, this may be the only solution, but
it should be cause of reevaluation of the design.
@chapter Guidelines
@section Encapsulation
Object operations should be accomplished via member functions. Users of the
object should have no access to the data which the object contains and the
data contained in the object should be as protected as possible. In addition,
member functions should not return pointers or references to any internal
protected data.
@section Comments
The format of comments is described in another document(work in progress). However,
it is often useful to use the @strong{C++} style of comments, @code{//}, as
opposed to the @strong{C} style of comments, @code{/* */}. By using
the @strong{C++} style, whole sections of code can be commented out using
@strong{C} style comments while debugging a section of code, without worrying
about nested @strong{C} comments.(See Meyers p.16) Of course another
technique for @emph{commenting out} a section of code is to put an
@code{#if 0} around it. However, here again care must be taken not to
begin or end the @code{#if 0} inside a @strong{C} comment.
@section Functions
@subsection Parameters and Return Values
Pass and return large objects by reference instead of by value, although
sometime objects @emph{must} be returned by value. This allows for much more efficient
invocation of functions because when objects are passed by value, the constuctors
for the object are called to create a duplicate copy of the object. This can be an
expensive operation. A reference to an object should be returned whenever
it is desirable for the return value to be used as an lvalue. For example,
it might be nice to have an @code{operator[]} for an array class. In this case,
the return type of the @code{operator[]} should be a reference, so that operations
like @code{A[2] = 9} can be performed. Likewise the @code{operator=} should
return a reference so that @code{a = b = c} is possible where a, b, and
c are some objects with the @code{operator=} defined.
However, sometimes an object must be returned as opposed to a pointer or
reference to a object. For example, when defining an addition operator for
complex numbers:
@example
class Complex @{
friend Complex operator+(const Complex &,const Complex &);
public:
Complex(float x=0, float y=0) : r(x), i(y)@{@};
private:
float r,i;
@}
inline Complex operator+(const Complex &a, const Complex &b)@{
return(Complex(a.r + b.r,a.i + b.i));
@}
@end example
@noindent
The important thing to note is that the @code{Complex} constructed in
@code{operator+} is destroyed as a temporary in expressions like
@code{d = a + b + c} where a, b, c, and d are all instances of the
@code{Complex} class. If the storage were dynamically allocated
within the @code{operator+}, there would be no way of freeing the
storage generated in the @code{a + b} sub-expression above
(See Meyers, pp. 78-84).
@subsection Operators
If you want conversion to happen on both the left and right hand sides of
an operator make the operator a friend instead of a member. For example,
if we extended the previous example to provide a @code{operator-} member
function, we would end up with a class that looks like:
@example
class Complex @{
friend Complex operator+(const Complex &,const Complex &);
public:
Complex operator-(const Complex &b){ return(Complex(r - b.r, i - b.i));};
Complex(float x=0, float y=0) : r(x), i(y)@{@};
private:
float r,i;
@};
inline Complex operator+(const Complex &a, const Complex &b)@{
return(Complex(a.r + b.r,a.i + b.i));
@}
@end example
@noindent
Both of these classes would work with expressions like @code{c = a + b} or
@code{c = a - b} where a, b, and c are instances of class @code{Complex}, but
in the case of, @code{c = 100 + a;} and @code{c = 100 - a}, only the
@code{operator+} would succeed because the constructor with defaults
would be applied to @code{100} to produce a temporary of type @code{Complex}.
In the case of @code{operator-} the compiler has no way of knowing to which
type the @code{100} should be converted (See Meyers, 66-71).
@subsection const and functions
Use const parameters for functions whenever possible, use const pointers and
references as return values to avoid the cost of object creation, and
use const member functions whenever the function does not modify the
object. Using the @code{const} specifier allows for further specification
of the type signatures of functions. By declaring the parameters as
@code{const}, the function accepts a wider number of arguments, i.e. both
const and non-const arguments. By declaring the function as const, the
potential users of the class are told that they can call the function without
worrying about modifications to the object. By returning const pointers and
references, the integrity of the object is maintained without the cost
of copying a portion of the object.
@section Parameterized Types/Templates
Parameterized types should be used to express a family of types. The most
obvious use is for parameterized container classes which will contain
heterogeneous data, e.g. a linked list. "Do Not use polymorphism
(derived classes with virtual functions) to implement parameterized
types."@footnote{Plum, p.60} Thus heterogeneous objects should not be
derived from a common class simply for the purpose of making a container
class or whatever from the common class. The primary reason this is
problematic is that the heterogeneous objects are free to mix in
the container class, and the only way to maintain a homogeneous collection
is by runtime type checks. Of course, sometimes this is the desired behavior.
@section Object Interface
The object interface should be a minimal interface, particularly the public
interface. Members should be as protected as possible, and only the
members designed for users of the class should be public. In addition,
if possible the members should be private, and only when they are intended
for use by derived classes and not for object users should they be made
protected.
@section Casts
Although some times type casts are necessary, they should, in general, be
avoided. The presence of casts undermines the @strong{C++} type system. Each
cast should be clearly documented. Also, the casting away of const variables
and objects should be avoided. In fact, attempted modifications to
a const object can either cause an addressing exception or modify the object
as specified. The choice is implementation dependent@footnote{ARM p.71}.
Downcasts from a virtual base class are always illegal. Sometimes, casts can
be eliminated by using virtual functions in the base class to perform the
necessary operations. Whenever possible the burden of performing operations
specific to derived classes should be shifted from a case statement on
``object type'' to virtual functions and inheritance.
@section Nested Types
Advantage can be gained by using nested type to avoid name conflicts in the
global name space. The type names can be made local to the class which
utilizes them. For example:
@example
class A @{
public:
enum data @{ IN, OUT, UP, DOWN @};
data getData()@{return x;@}
void setData(data z)@{ x = z;@}
A() : x(IN)@{@};
private:
data x;
@};
main()@{
A a;
A::data t;
t = a.getData();
a.setData(A::UP);
@}
@end example
@noindent
Here @code{data} is a type local to the class @code{A}, but since it is
public it can still be used as a type name and the elements of the
enumeration can be accessed by prefacing them with the class specifier,
@code{A::}. The elements of the enumeration, @code{IN, OUT,} etc. must
be unique, i.e. there could not be another enumeration with any of the
same elements as the @code{data} enumeration within @code{A}.
@section Error Handling
Exceptions will provide the primary means of flaging and handling errors.
An exception class, will be built for general use. It will be based upon
the OpenSys Inc., @emph{Exc C++ exception enabling library} and the
1988 Usenix paper on exceptions @footnote{Miller, Exception Handling Without
Language Extensions, 1988 Usenix Proceedings, C++ Conference, pp.327-341}.
@section IO Streams
It is often advantageous to use the @code{<iostream.h>} package because it
is extensible. That is the designer of a package can design functions to
print out his/her classes.
@section Library Names
To identify members of libraries, e.g. math, system, etc., the classes
or stand alone public functions belonging to these libraries should be
prefixed with one ore two letters to denote their membership to the
library. Thus, for example:
@example
class mSin @{
...etc...
@};
@end example
@noindent
would belong to the @sc{aips++} math library. In general, mixed case will
be used to distinguish words within an identifier, e.g.
@code{MyLongIdentifier}.
@section Format
Many feel that it is important to have a format guidelines. These are
standards which make the code ``more readable'' by having a indicating
how control structures and code blocks should look. There are three
primary standards @footnote{Plum p.181}. Of these, the Kernighan and
Ritchie format is the control structure format for @sc{aips++}.
So for example:
@example
main(int argc, char *argv[])@{
char *x;
for (int i=1; i < argc; i++) @{
int j = i+1;
while (j < argc) @{
if (strcmp(argv[j],argv[i]) < 0) @{
x = argv[i];
argv[i] = argv[j];
argv[j] = x;
@} else @{
// argv[i] = argv[i]; // Format of else
@}
j += 1;
@}
@}
for (i = 0; i < argc; i++) @{
cout << argv[i] << "\n";
@}
@}
@end example
@chapter Warnings
@section Machine Size and Ordering Dependencies
@subsection Size of Builtin Types
Concerning the sizes of the standard integral types the following are
guaranteed @footnote{Stroustrup 2ed p50}:
@itemize @bullet
@item
@code{1 == sizeof(char) <= sizeof(int) <= sizeof(long)}
@item
@code{sizeof(float) <= sizeof(double) <= sizeof(longdouble)}
@item
@code{sizeof(@emph{t}) == sizeof(signed @emph{t}) == sizeof(unsigned @emph{t})} where t is one of the basic types
@end itemize
@noindent
Additionally, the following are the minimum sizes guaranteed for the integral
types @footnote{Stroustrup 2ed p50} and the floating point
types @footnote{ARM p.24}:
@itemize @bullet
@item
@code{char} --- 8 bits
@item
@code{short} --- 16 bits
@item
@code{long} --- 32 bits
@item
@code{float} --- 32 bits
@item
@code{double} --- 64 bits
@item
@code{long double} --- 96 bits
@end itemize
@noindent
The size of an @code{int} is typically 16 to 32 bits, but this size is not
guaranteed (32 bits in all probability for machines running @sc{aips++}). The
size of the integral and floating point types are available in the header
files @code{<limits.h>} and @code{<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:
@example
short i = 25;
char *cptr;
cptr = (char *) &i;
@end example
@noindent
One cannot anticipate the byte to which @code{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 @code{<netinet/in.h>},
e.g. htonl, etc.
@subsection 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 @code{offsetof} macro in @code{<stddef.h>}(see p. 84 Plum).
So for a struct like:
@example
struct mystruct @{
int x;
int y;
@};
@end example
@noindent
@code{offsetof(mystruct, y)} might yield 4.
@subsection 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:
@example
short crlf = '\r\n';
@end example
@noindent
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.
@example
#define CHAR2(a, b) (((a) << CHAR_BIT) + (b))
@end example
@noindent
portably combines two characters @footnote{Plum, p.76}.
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 @footnote{Plum, p.86}:
@enumerate
@item
@code{static char fname[] = "/tmp/edXXXXXX"; mktemp(fname);}
@item
@code{mktemp("/tmp/edXXXXXX")}
@end enumerate
@section Creation and Deletion
@subsection 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 @footnote{Plum, p.138}. There are ways to ensure
a certain initialization order @footnote{Schwarz, "Initializing Static
Variables in C++ Libraries", C++ Report. Vol1 No2, Feb. 89, pp1-4.}.
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:
@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) @{@};
@};
@end example
@noindent
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 @footnote{Meyers, p.42}.
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.
@subsection 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:
@example
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;
@}
@end example
@noindent
the call to @code{delete s} only causes the destructor for the first
@code{s[0]} to be called. The space allocated for @code{there} and
@code{friend} is lost forever. The correct way to delete @code{s} is
@code{delete [] s} @footnote{Meyers, p.19}.
Another interesting point is that for an array of objects to be allocated
via @code{new} a @emph{default constructor} has to be defined, i.e. a
constructor without parameters @footnote{ARM p.61}. Specifying a constructor
with all default parameters is not sufficient.
@subsection 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
@footnote{Plum, p.208}:
@itemize @bullet
@item
There are classes derived from the base class.
@item
The destructor in the derived class is different form the base class
destructor.
@item
Derived class objects may be deleted via a pointer or reference to base
class objects.
@end itemize
@noindent
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.
@subsection 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).
@subsection New and Delete
Some problems can arise with classes which define the operators @code{new} and
@code{delete} with more than one argument because these definitions hide
the global or base class definitions. For example @footnote{Meyers, p.25}:
@example
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;
@}
@end example
@noindent
The first @code{new} succeeds with no problems. The second however causes an
error because the global @code{new} which takes one parameter, @code{size_t},
is hidden by the local @code{new} in class @code{X}. This problem can be
corrected by either calling the global new explicitly, @code{X *px2 = ::new X},
or by providing a @code{new} operator in @code{X} which takes only one
parameter. It is also a good idea to supply both @code{new} and
@code{delete} if one is needed to avoid future maintenance problems.
@section Copy Operator
Be aware that if you don't explicitly disallow the copy operator,
@code{@emph{Type}::operator=(@emph{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 @code{friends} from using it.
@bye