Style Standards for C++ Programs The purpose of a printed listing of a program is to communicate the exact actions that the program carries out. Unfortunately, people are not machines, so they cannot comprehend well logical combinations that exceed a certain, modest size. This is typically something less than a page of code. Therefore, the programmer must supplement the code by explaining the purpose of each section of code whose meaning is not immediately discernible. Part of understanding a code section is understanding its relation to code that may be far removed from it. Thus, proper documentation also includes explanation of the data that the code receives from its exterior and supplies other parts of the program. In large systems of software, as are typtical today, it is imperative that the system designer and the user comprehend the semantics (meaning, purpose) of the various parts of the system and how they are interrelated. The system itself, then, must be designed so that its structure communicates this meaning clearly. The present document is to provide you with guidelines to follow in documenting your programs. It also includes guidelines that you should follow when designing a system and during coding. Remember, the code you may have to understand may be your own! The items in this document are written in reverse order of the logical and chronological flow, so read them from the bottom up. Thus, newly added items will appear at the top, while older items will be pushed down the stack. ====================================================================== y 27. Deferred (abstract) classes are used when it is not known at the time of the construction of the class system how to code the definition of a method. That is, the algorithm in the method may depend strongly on the situation at the time the user is coding the application program, not at system construction. 26. In C++, when several sibling classes inherit from a common ancestor, make the inheritance connection 'virtual'. That is specify each ancestor, A, by virtual public A following the colon on the declaration line of each descendent class. This will help to avoid introducing the ambiguity treated in 25. if, later, the system developer needs to inherit from more than one of the descendents. 25. Always be cautious about introducing multiple inheritance into a system. Is there a better way to structure the classes? Strive for ancestors that have no attributes (data fields or methods) in common. If they exist, move the common features into a higher level ancestor. Explicitly AVOID creating ancestors of the same level (siblings) that share a data field identifier, but whose value depends upon the class it is declared in. This creates ambiguity. The same holds for shared function names. 24. When using exceptions in C++, always plan so that, whenever possible, a dynamic data structure is declared as a data field of a class. Then, the class utilities (i.e., destructor, constructors and = ) are responsible for managing the structure, not the programmer of some application. This supports reliability and prevents errors. 23. Exceptions are a mechanism to minimize chaining reports in the case of catastrophic errors. They should be used to escape from the damaged execution sequence to code that reports as much information as possible concerning the nature of the error and performs clean up duties, such as collecting dynamic garbage and closing files. The code finally transfers control to the appropriate procedure (generally, the O. S.). 22. For security, every statement that can fail should be preceded by a check to assure the correct values (preconditions) necessary for the successful completion of the statement are valid. In certain circumstances, the output vales (postconditions) may be checked for validity instead. When there are nested function invocations (always!), the occurrence of any error condition detected by these tests should be reported to the immediately preceding calling environment. This implies a chain of error reports to the top root of the hierarchy. 21. Use virtual destructors in inheritance hierarchies to facilitate the selection of the proper destructor when using "polymorhic" references. 20. Some rules for designing an inheritance hierarchy of classes: a. The immediate descendent classes of an ancestor class (siblings) together contain as objects all of the ancestor's objects. This means that all ancestor classes are abstract classes. b. No object can belong to more than one sibling class. c. All attributes that sibling classes share are defined in their ancestors, i.e., no two of them define the same method or data member. This criterion is recommended, but not required. Circumstances may warrant not adhering to it. 19. Document your code so others can use it. That is how you become famous (if not rich) and beloved by all programmers. Refer to the writing guidelines in the documents "The User's Guide" and "The Programmer's Guide" 18. Use "object think". When designing a method, think of the data members as general objects, not as strings, floats, etc. Try to assume knowledge of only the data members of the class to which the method is a member function. Delegate all operations on the components of a data member to the methods of the class to which the member belongs. 17. When initializing a variable in its declaration, use the () notation in stead of the = notation. This is for consistency. Also, the initialization, which invokes a constructor, looks more like the constructor's prototype, which helps to reduce errors and confusion. For example: Type variable(value); rather than Type variable = value; 16. When writing a constructor, initialize all non dynamic data members by means of the initializer list following the prototype and : 15. If the values that a data member of a class may take all must fall within a restricted range, the constructors should check that all data assigned to the member belong to this range. 14. In the declaration of a class, the members (functions and data) that are intended for public use should appear first, followed by those that are restricted to use only by the class's other member functions. 13. The constructor signatures should appear first in a class declaration, beginning with your version of the default constructor, if any, followed by your destructor, if any. Below these should appear the signatures of any functions that are intended for the public to use. 12. The definitions of a class should be separated into two files: a header file and an implementation file. The header should contain the class declaration, which contains the function prototypes and all static data structures making up the class's object. Any definitions of identifiers necessary to complete the declaration of these members should appear in the class declaration also. The implementation of each member function should be placed in the implementation file, which will #include the header file. Only one class should be defined in such a pair of files. The only exception to this would be for small, supporting classes that are used only by the class sharing the file. 11. To provide understandable code, it is important to use identifiers (e.g., variable, function and constant names) that are suggestive of their use. This is particularly true of C and C++. To aid in clarity, compound identifiers can be constructed using Capitals and _ . For example: NumberOfChickens() number_of_foxes 10. When writing function signatures, list output parameters first, input/output parameters next and input parameters on the right. An "output" parameter is one through which the function passes data out to the calling procedure. An "input" parameter is one through which it only receives data, and an "input/output" parameter is one that serves both purposes. 9. Just following the prototype and just before the implementation of each function, provide a description that includes the following information: Input parameters: For the parameters that supply data to the procedure, list the parameter names and their purposes. Output parameters: For the parameters which the procedure changes the values of, list the parameter names and their purposes. Return value: describe the purpose of the returned value, or say None if the value is of void type. Purpose: Describe briefly here what the procedure does. 8. Do not expend a great deal of time in working out neat, concise, highly efficient code, especially if no one else can understand easily your brilliant logic. It is better to write code that is easily understood and quickly written. Later, when the program is running and if performance or code size is a problem, you can tune the program by revising the most time and memory consuming parts. This does not excuse the use of clearly inefficient code when persons of your level of experience should be aware of better approaches. 7. Declarations of data structures (including variables) should be grouped together at the beginning of the file or block { } that determines their scope. Each definition should be accompanied by an explanation of the purpose of the item. Avoid declarations of variables in the code body. In limited cases, where a variable has a scope over a very short block and has little significance in the problem, such as a loop counter, an inline definition is permissible without explanation. However, a comment section near the declarations of the other variables for the file or immediately larger containing block should list these identifiers with a brief explanation of their use, e.g. "while-loop counters". Separate declarations in a block or file from the following code by blank lines or strings of bars, etc. 6. Every function that you define must have a prototype, more generally called a "signature". You should follow each prototype by a short description of what the input to the function is, what the function does, and what its output is. When the function's definition is separated significantly from the prototype, this information should be pasted before the actual function definition also. The prototype declarations and function definitions should be grouped at the beginning of the block { } or file that determines their scope. 5. Do not clutter your program by writing comments for each and every statement. This is over killing oneself. 4. Write a short description, at most two or three sentences, before or alongside of any function CALLs explaining what the function is supposed to accomplish at that point of the code. You should do the same before any complex section or block of code that has a specific purpose and before shorter sections whose logic is not clear from a quick reading of the code. Long remarks should precede the code, while short ones may be placed alongside it. The quality of your documentation depends greatly on how clearly and concisely you are able to state the essential idea of what the code does. Careful selection of words is important. Correct English grammar, spelling and punctuation are important. 3. Wherever possible reduce the complexity of a program by writing separate compilable modules and linking them to be one executable program. This will help you in understanding what you are doing. Keeping everything in chunks that you can comprehend without great effort is an important problem solving technique. 2. All programs must be submitted as source code readable in one of the three editors mentioned in 1. They must compile and execute under the version of g++ on klingon. 1. All documentation must be written in a text editor and turned in as a text file that is readable by one of the text editors VI, EMACS or PICO. The Instructor may make exceptions to this rule under certain circumstances.