Versions Compared
Key
- This line was added.
- This line was removed.
- Formatting was changed.
Page Tree | ||||
---|---|---|---|---|
|
5. Class
5.1 Class Declaration (Header File)
- Public data should not be used without overriding efficiency justification.
- Provide access methods (Get/Set) for data as needed for access by other classes or code (e.g., GetPosition(), SetRadius())
- Declare a destructor to be virtual if there is any possibility that a class could be derived from this class (particularly if there are any virtual methods declared for this class).
- Declare a method to be virtual if you expect it to be overridden by the derived classes. The most prominent reason why a virtual function will be used is to have a different functionality in the derived class. The difference between a non-virtual member function and a virtual member function is: the non-virtual member functions are resolved at compile time in what is known as static or compile - time binding, and the calls to the virtual member functions are resolved during run-time using a mechanism known as dynamic binding.
Notes:
Since GMAT supports plug-in modules that may be written by groups that do not have write access to the repository, we ought to code GMAT so that these outside groups can extend the system without either granting write access or making a local fork version for the purpose of making the methods extensible through a virtual declaration.
- When declaring a function, put its return type on the same line as the function name. However, if the return type is very long, it is preferable to put the method name on the next line, lined up with the list of methods.
int GetParamCount();
virtual std::string GetRefObjectName(const Gmat::ObjectType type) const; virtual const StringArray& GetRefObjectNameArray(const Gmat::ObjectType type);
- Include preprocessor commands to avoid multiple definitions of items in a header file. Capitalize the preprocessor command the same way as the class name for easier future text substitution.
For the AnalyticalModel class:
#ifndef AnalyticalModel_hpp #define AnalyticalModel_hpp ... // class definition or type definitions (etc.) ... #endif // AnalyticalModel_hpp
5.1.1 Required Methods for a Class
Always include the following methods for each class, to avoid having the compiler declare them for you. Declare them private (and possibly unimplemented) to limit or disable usage.
- default constructor
- copy constructor
- destructor
- assignment operator
5.1.2 Class Method/Function Declaration Layout
The class declaration should include public, protected, and (if applicable) private sections (in that order). Since the user of the class needs the class interface, not the implementation, it make sense to have the public interface first.
Example:
class SolarSystemBody { public: ... protected: ... private: ... };
5.1.3 Include
- Include statements must be located at the top of a file only.
- Include statements should be sorted and grouped. Sorted by their hierarchical position in the system with low level files included first.
- Use C++ libraries, instead of C libraries, whenever possible. For example,
use #include <iostream> instead of #include <stdio.h>
Notes:
This guideline may be ignored in cases of optimization, where the C library routines are proved to be more efficient than the C++ library routines)
- For system include files, put a comment explaining why a particular file was included.
- Header files should be included only where they are needed and whenever they are needed for clarity. i.e. the user should be able to easily follow the code to determine the origin of methods or variables used in the code.
- Wherever possible, extern declarations (of global data) should be contained in source files instead of header files.
- Use extern "C" if referencing C external variables for functions, when necessary.
- When extern data or constants are included in a header file, remember to compile and link the .cpp file(s) that contain the actual definitions of the constants or externs into your program.
For Portability
You should also avoid using directory names in the include directive, since it is implementation-defined how files in such circumstances are found. Most modern compilers allow relative path names with / as separator, because such names has been standardized outside the C++ standard, for example in POSIX. Absolute path names and path names with other separators should always be avoided though.
The file will be searched for in an implementation-defined list of places. Even if one compiler finds this file there is no guarantee that another compiler will. It is better to specify to the build environment where files may be located, since then you do not need to change any include-directives if you switch to another compiler.
#include "inc/MyFile.h" // Not recommended #include "inc\MyFile.h" // Not portable #include "/gui/xinterface.h" // Not portable #include "c:\gui\xinterf.h" // Not portable http://hem.fyristorg.com/erny/industrial/IndustrialStrength.14.html#40037
5.1.4 Inlining
- Be careful about inlining. If your compiler has an inlining switch, prefer the use of that to actually including methods' implementations in the header file. (Also, compilers are not up-to-speed on compiling when methods are inlined in the header - in some cases, a compiler will generate a larger program because of this).
- Inline member functions can be defined inside or outside the class definition. The second alternative is recommend. The class definition will be more compact and comprehensible if no implementation can be seen in the class interface.
class X { public: // Not recommended: function definition in class bool insideClass() const { return false; } bool outsideClass() const; };
// Recommended: function definition outside class inline bool X::outsideClass() const { return true; }
5.1.
5Class5 Class Header File Layout
Header files should include items in this order:
- SVN keyword -Class name banner -Header file prolog -Preprocessor #ifndef command -System include files -Application include files -Constant declarations -Class declaration -Non-member functions (global functions) -Preprocessor #endif command
Example:
//$Id$ //----------------------------------------------------------------------------- // Class Name //----------------------------------------------------------------------------- // Header File Prolog // ... // ... //----------------------------------------------------------------------------- #ifndef ClassName_hpp #define ClassName_hpp
#includes ... Constant declarations ...
class ClassName { public: ... protected: ... private: ... };
//------------------------------------- // global declarations //------------------------------------- ... ...
#endif // ClassName_hpp
5.
2Class2 Class Definition (Source File)
5.2.
1Constructors1 Constructors
- Avoid doing any real work in a constructor. Initialize variables and do only actions that can't fail. Object instantiators must check an object for errors after construction.
- Avoid throwing exceptions from constructors.
- All member data should be initialized in a constructor, not elsewhere, whenever possible.
5.2.2Exceptions
- Use exceptions for truly exceptional conditions, not for message passing (i.e. use exceptions when processing cannot continue without user action).
?For Efficiency
- Catch exceptions by reference.
5.2.3Class Method/Function Definition Layout
- Methods/functions should be defined in the order in which they appear in the class declaration.
- Always initialize all variables.
- The function signature, its return type, and the argument names in the definition (implementation) should match its declaration (prototype) exactly.
- When defining a function's implementation, a long return type may go on a line above the function name.
int AttitudeModelClass::GetModelNumber() { ... }
CosineMatrix
CosineMatrix::GetInverse() const { ... }
? For Efficiency
- Minimize the number of constructor/destructor calls: this means minimize the number of local objects that are constructed; construct on returning from a method, rather than creating a local object, assigning to it, and then returning it; pass large objects by const reference; etc.
- Initialize member data in an initialization list. It is necessary that the order of the items in the initialization list match the order of declaration; also, initialize base class data first (if it is not already initialized in the base class code)
MaString::MaString(const char *string1, unsigned int len1, const char *string2, unsigned int len2) : lengthD(len1 + len2), caseSensitiveD(true) { ... }
5.2.4Class Source File Layout
- The source file should contain the following sections:
- SVN keyword -Class name banner -Source file prolog
#includes -#defines -Static variable initialization -Source file method prolog followed by implementation
Example:
//$Id$ //----------------------------------------------------------------------------- // Class Name //----------------------------------------------------------------------------- // Source File Prolog // ... //----------------------------------------------------------------------------- #includes ... #defines ...
//----------------------------------- // static data //----------------------------------- const Real ClassName::REAL_UNDEFINED = -987654321.0123e-45; const Integer ClassName::INTEGER_UNDEFINED = -987654321;
//----------------------------------- // static functions //-----------------------------------
//----------------------------------- // public methods //-----------------------------------
//----------------------------------------------------------------------------- // source file method prolog // ... //-----------------------------------------------------------------------------
Implementation code ... ...
//----------------------------------- // protected methods //-----------------------------------
//----------------------------------------------------------------------------- // source file method prolog // ... //-----------------------------------------------------------------------------
Implementation code ... ...
//----------------------------------- // private methods //-----------------------------------
//----------------------------------------------------------------------------- // source file method prolog // ... //-----------------------------------------------------------------------------
Implementation code ... ...
//-----------------------------------------------------------------------------
h8.Anchor Templates Templates 6Anchor Miscellaneous Miscellaneous Miscellaneous
6.1Templates
Templates are in one respect very similar to an inline function. No code is generated when the compiler sees the declaration of a template; code is generated only when a template instantiation is needed.
A function template instantiation is needed when the template is called or its address is referenced and a class template instantiation is needed when an object of the template instantiation class is declared.
The template specifier for a template class should be placed alone on the line preceding the "class" keyword or the return type for a function. Template parameters should be in upper case.
// template declaration template<class T> class ListTemplate { public: T front(); ... }; // template definition template<class T> T ListTemplate<T>::front() { ...; }
A big problem is that there is no standard for how code that uses templates is compiled. The compilers that require the complete template definition to be visible usually instantiate the template whenever it is needed and then use a flexible linker to remove redundant instantiations of the template member function. However, this solution is not possible on all systems; the linkers on most UNIX-systems cannot do this.
Even though there is a standard for how templates themselves should be compiled, there are still many compilers that do not follow the standard. Some compilers require the complete template definition to be visible, even though that is not required by the standard.
This means that we have a potential portability problem when writing code that uses templates.
=>We recommend that you put the implementation of template functions in a separate file, a template definition file, and use conditional compilation to control whether that file is included by the header file. A macro is either set or not depending on what compiler you use. An inconvenience is that you now have to manage more files. Only inclusion of the header file should be needed.
#ifndef QueueTemplate_hpp #define QueueTemplate_hpp
template <class T> class QueueTemplate { public: QueueTemplate(); // ... void insert(const T &t); };
//--------------------------------- // Template Definition //--------------------------------- #ifndef EXTERNAL_TEMPLATE_DEFINITION #include "QueueTemplate.cpp" #endif
#endif
6.2Program Files
- Files should all begin with a file prolog (see below).
- Organize a program into two types of files as follows:
-A class declaration -Any global type declarations -Any exceptions -Any typedefs
- Any includes for template files -ENUM type definitions
Source File (.cpp) - should contain:
-Any Static and/or Constant data values -Method definitions (implementation)
- Organize header files by class (one class declaration per header file) or by logical grouping of functions (e.g. RealUtilities)
- The main procedure should reside in its own file.
- For source files which contain related functions (utilities, for example), follow guidelines for putting functions in some meaningful order.
- Do not use rows of asterisks to separate functions.
- There should be only one class or namespace per .hpp/.cpp pair. There may be exceptions to this, though, if a set of namespaces or classes are small and may be logically grouped together.
- Use ANSI/ISO C++ whenever it is available.
- When optimizing, some thought must be given to portability issues.
- Consider optimizations right from the start, as it is much harder to go back and redesign or recode later.
- Pass "large" arguments (instances of classes or structs) to a function by const reference when the arguments don't need to be modified and pass as reference when they need to be modified
- Place typedefs for all common types (e.g. Real defined as double, Integer defined as int) in a central header file, accessible by all code, for easier portability to other platforms and to higher precision types.
- For efficiency, minimize the number of constructor/destructor calls: this means minimize the number of local objects that are constructed; construct on returning from a method, rather than creating a local object, assigning to it, and then returning it; pass large objects by const reference; etc.
- For efficiency, use exceptions only for truly exceptional conditions, not for message passing
- Use embedded assignments when they are proven to be more efficient than not using them.
6.5Extern statements / External variables
- Avoid using extern statements in the header file. Whenever possible, the source files referencing the global data should "extern" the needed global data, so that reader will know which variables are declared external to that source file.
- Avoid declaring non-static external variables. Variables needed by more than one file should appear in a .cpp file and be externed in a source file.
- Include a preprocessor directive in the main header file, accessed by all code, e.g.
6.7Mixing C and C++
- Include files which contain code that is accepted by both C and C++ compilers should have the file name extension ".h".
- Make the header files work correctly when they are included by both C and C++ files. If you start including an existing C header in new C++ files, fix the C header file to support C++ (as well as C), don't just extern "C" { } the C header file.
#ifdef __cplusplus extern "C" { #endif int existingCfunction1(…); int existingCfunction2(…); #ifdef __cplusplus } #endif
6.8 SVN Keywords
- If SVN is used for configuration management, the top of every file should contain the following lines (or those agreed upon by the project/team):
- The command to replace the keyword with actual data is:
6.9README file
- A README file should be used to explain what the program does and how it is organized and to document issues for the program as a whole. For example, a README file might include:
6.10 Makefiles
- Makefiles are used on some systems to provide a mechanism for efficiently recompiling C++ code. With makefiles, the make utility recompiles files that have been changed since the last compilation. Makefiles also allow the recompilation commands to be stored, so that potentially long CC commands can be greatly abbreviated.
-Lists all files that are to be included as part of the program. -Contains comments documenting what files are part of libraries. -Demonstrates dependencies, e.g., source files and associated headers using implicit and explicit rules.
6.11Standard Libraries
- A standard library is a collection of commonly used functions combined into one file. Examples of function libraries include <iostream> which comprises a group of input/output functions and <math> which consists of mathematical functions. When using library files, include only those libraries that contain functions that your program needs. You may create your own libraries of routines and group them in header files.
- Use C++ standard libraries, instead of C libraries, whenever possible, unless it is more efficient to use C libraries.
The use of namespaces minimizes potential name clashes in C++ programs and libraries and eliminates the use of global types, variables, etc.
Clashable names include: external variable names, external function names, top-level class names, type names in public header files, class member names in public header files, etc. (Class member names are scoped by the class, but can clash in the scope of a derived class. Explicit scoping can be used to resolve these clashes.)
It is no longer necessary to have global types, variables, constants and functions if namespaces are used. Names inside namespaces are as easy to use as global names, except that you sometimes must use the scope operator. Without namespaces it is common to add a common identifier as a prefix to the name of each class in a set of related classes.
It is recommended not to place using <namespace> directives at global scope in a header file; instead place it in a source file. This can cause lots of magic invisible conflicts that are hard to track since it will make names globally accessible to all files that include that header, which is what we are trying to avoid. Inside an implementation file, using directives are less dangerous and sometimes very convenient. On the other hand, too-frequent use of the scope operator is not recommended. The difference between local names and other names will be more explicit, but more code needs be rewritten if the namespaces are reorganized.
6.13Standard Template Library (STL)
- Use Standard Template Library components, when available.
- The specification for operator "new" was changed by the standardization committee, so that it throws an exception of type std::bad_alloc when it fails. Therefore, the code may catch this exception, rather than check for a NULL value. e.g.
int someFunction() { try { SomeClass *someClassList = new SomeClass[size]; } catch (std::bad_alloc &ex) { … } }
A.1Example of a header file.
//$Id:$ //------------------------------------------------------------------------------ // EopFile //------------------------------------------------------------------------------ // GMAT: General Mission Analysis Tool. // // Copyright (c) 2002-2011 United States Government as represented by the // Administrator of The National Aeronautics and Space Administration. // All Other Rights Reserved. // // Developed jointly by NASA/GSFC and Thinking Systems, Inc. under // MOMS Task order 124. // // Author: Wendy C. Shoan/GSFC/MAB // Created: 2005/01/26 // /**
#ifndef EopFile_hpp #define EopFile_hpp
#include "gmatdefs.hpp" #include "Rmatrix.hpp"
namespace GmatEop { enum EopFileType { EOP_C04, FINALS }; };
class GMAT_API EopFile { public: // default constructor EopFile(const std::string &fileName = "eopc04.62-now", GmatEop::EopFileType eop = GmatEop::EOP_C04); // copy constructor EopFile(const EopFile &eopF); // operator = const EopFile& operator=(const EopFile &eopF); // destructor virtual ~EopFile();
// initializes the EopFile (reads it and stores the data) virtual void Initialize();
// method to return the name of the EOP file virtual std::string GetFileName() const;
// method to return the UT1-UTC offset for the given UTCMjd virtual Real GetUt1UtcOffset(const Real utcMjd);
// method to return JD, X, Y, LOD data (for use by coordinate systems) virtual Rmatrix GetPolarMotionData(); // interpolate x, y, and lod to input time virtual bool GetPolarMotionAndLod(Real forUtcMjd, Real &xval, Real &yval, Real &lodval);
protected:
static const Integer MAX_TABLE_SIZE;
GmatEop::EopFileType eopFType; std::string eopFileName; Integer tableSz;
/// table of polar motion data : MJD, X, Y, LOD Rmatrix polarMotion;* /// vector of UT1-UTC offsets : MJD, offset Rmatrix ut1UtcOffsets;*
Real lastUtcJd; Real lastOffset; Integer lastIndex;
bool isInitialized;
bool IsBlank(const char aLine);*
// Performance code Integer previousIndex;
}; #endif // EopFile_hpp A.2 Example of a source file.
//$Id:$ //------------------------------------------------------------------------------ // EopFile //------------------------------------------------------------------------------ // GMAT: General Mission Analysis Tool. // // Copyright (c) 2002-2011 United States Government as represented by the // Administrator of The National Aeronautics and Space Administration. // All Other Rights Reserved. // // Developed jointly by NASA/GSFC and Thinking Systems, Inc. under // MOMS Task order 124. // // Author: Wendy C. Shoan // Created: 2005/01/26 // /**
#include <iostream> #include <fstream> #include <sstream> #include <iomanip> #include "gmatdefs.hpp" #include "EopFile.hpp" #include "TimeTypes.hpp" #include "UtilityException.hpp" #include "RealUtilities.hpp" #include "MessageInterface.hpp"
//#define DEBUG_OFFSET //#define DEBUG_EOP_READ //#define DEBUG_EOP_INITIALIZE
//------------------------------------------------------------------------------ // static data //------------------------------------------------------------------------------ const Integer EopFile::MAX_TABLE_SIZE = 50405; // up to year >= 2100
//------------------------------------------------------------------------------ // public methods //------------------------------------------------------------------------------
//--------------------------------------------------------------------------- // EopFile(const std::string &fileName, // GmatEop::EopFileType eop = GmatEop::EOP_C04); //--------------------------------------------------------------------------- /**
//--------------------------------------------------------------------------- // EopFile(const EopFile &eopF); //--------------------------------------------------------------------------- /**
//--------------------------------------------------------------------------- // EopFile& operator=(const EopFile &eopF) //--------------------------------------------------------------------------- /**
//--------------------------------------------------------------------------- // ~EopFile() //--------------------------------------------------------------------------- /**Destructor.* */ //--------------------------------------------------------------------------- EopFile::~EopFile() { delete polarMotion; delete ut1UtcOffsets; }
//------------------------------------------------------------------------------ // void Initialize() //------------------------------------------------------------------------------ /**
… … (implementation details go here) …
isInitialized = true; }
//--------------------------------------------------------------------------- // std::string GetFileName() const //--------------------------------------------------------------------------- /**
//--------------------------------------------------------------------------- // Real GetUt1UtcOffset(const Real utcMjd) //--------------------------------------------------------------------------- /**
if (lastUtcJd == (utcMjd + GmatTimeConstants::JD_NOV_17_1858)) return lastOffset;
… … (implementation details go here) …
return off; }
//--------------------------------------------------------------------------- // Rmatrix GetPolarMotionData() //--------------------------------------------------------------------------- /**
//--------------------------------------------------------------------------- // bool GetPolarMotionAndLod(Real forUtcMjd, Real &xval, Real &yval, // Real &lodval) //--------------------------------------------------------------------------- /**
… … (implementation details go here) …
return true; }
//------------------------------------------------------------------------------ // bool IsBlank(char aLine)* //------------------------------------------------------------------------------ /**
- @author {list of authors} Starts a paragraph where one or more author names may be entered.
- @class <name> [<header-file>] [<header-name>] Indicates that a comment block contains documentation for a class with name. Optionally a header file and a header name can be specified.
- @date {date description} Starts a paragraph where one or more dates may be entered.
- @defgroup <name> (group title) Indicates that a comment block contains documentation for a group of classes, files or namespaces.
- @endlink This command ends a link that is started with the @link command.
- @enum <name> Indicates that a comment block contains documentation for an enumeration.
- @example <file-name> Indicates that a comment block contains documentation for a source code example.
- @exception <exception-object> {exception description} Starts an exception description for an exception object with name <exception-object>. Followed by a description of the exception.
- @file [<name>] Indicates that a comment block contains documentation for a source or header file with name <name>.
- @fn (function declaration) Indicates that a comment block contains documentation for a function (either global or as a member of a class).
- @include <file-name> This command can be used to include a source file as a block of code. The command takes the name of an include file as an argument.
- @interface <name> Indicates that a comment block contains documentation for an interface with name <name>.
- @link <link-object> This command can be used to create a link to an object (a file, class, or member) with a user specified link-text. The link command should end with an @endlink command.
- @name (header) This command turns a comment block into a header definition of a member group.
- @namespace <name> Indicates that a comment block contains documentation for a namespace with name <name>.
- @package <name> Indicates that a comment block contains documentation for a Java package with name <name>.
- @param <parameter-name> {parameter description} Starts a parameter description for a function parameter with name <parameter-name>. Followed by a description of the parameter.
- @return {description of the return value} Starts a return value description for a function.
- @retval <return value> {description} Starts a return value description for a function with name <return value>. Followed by a description of the return value.
- @struct <name> [<header-file>] [<header-name>] Indicates that a comment block contains documentation for a struct with name <name>.
- @test {paragraph describing a test case} Starts a paragraph where a test case can be described.
- @union <name> [<header-file>] [<header-name>] Indicates that a comment block contains documentation for a union with name <name>.
- @var (variable declaration) Indicates that a comment block contains documentation for a variable or enum value (either global or as a member of a class).
- @version {version number} Starts a paragraph where one or more version strings may be entered.
- @warning {warning message} Starts a paragraph where one or more warning messages may be entered.
1."C Style Guide", Doland, J. et. al., SEL-94-003, Software Engineering Laboratory Series, Goddard Space Flight Center, August 1994.
2.Effective C++, Meyers. S., Addison-Wesley Professional Computing Series, 1992.
3.C++ Primer, 2nd Edition, Lippman, S., AT&T Bell Laboratories, 1991.
4."Programming in C++ Rules and Recommendations", Henricson, M. and Nyquist, E., Ellemtel Telecommunication Systems Laboratories, 1990-1992.
5.C++ Style Guide, Version 1.0, Software and Automation Systems Branch, Goddard Space Flight Center, July 1992.
6."C++ Programming Style Guides", Eckel, B., UNIX Review, March 1995.
7."C++ Coding Standard", http://www.chris-lott.org/resources/cstyle/CppCodingStandard.html.