Graphisoft®

C/C++ Style GuideVersion: 1.1

C++ 스타일 가이드

1. 소개 

이 문서에는 주로 소스 코드를 작성하는 공식 요구사항을 설정하는 Graphisoft의 코딩 표준이 포함되어 있습니다. 또한 내용에 대한 몇 가지 규칙과 권고사항을 기술하고 있습니다. 이러한 규칙에 복종해야 합니다; 기본적으로 모든 선언적 또는 명령적 문장은 '권고'(또는 피할 수 있는, 선택적 등)를 제외하고 규칙이다. 권고사항은 명시적으로 서술되어 있습니다.
 
 

2. 명명 규칙
 

  1. 아래에서 허용되는 접두사 외에 다른 접두사를 사용하는 것은 금지되어 있습니다.
    밑줄 사용(_)은 금지되어 있지 않지만 가능하면 피하십시오. 이것은 이름의 첫 번째 또는 마지막 문자로 사용할 수 없습니다.
    표준 C 함수 이름(예: floor, abs)은 이름으로 사용해서는 안 됩니다.
     
  2. #define 이름
    대문자로 작성해야 합니다; 2개 이상의 단어로 구성된 이름은 단어 사이에 밑줄(_)로 분리해야 합니다.
    예: NULL, PLATFORM, DEBUG_LEVEL_2, REFERENCE_CHECK
    NULL은 0 포인터를 표시하기 위해 사용되어야 합니다.
     
  3. Type 이름 (struct, enum, typedef, class, namespace, template parameter)
    첫 글자는 대문자입니다; 모든 새로운 단어 역시 대문자로 시작해야 합니다. 인터페이스 이름의 경우 무조건 "I" 접두사를 넣어야 합니다.
    예: Vector2D, Array, SymbolSet, Char, IComparable
     
  4. Variable 이름
    소문자로 시작합니다; 모든 새로운 단어 역시 대문자로 시작해야 합니다. 모든 멤버 변수들은 "m" 접두사(예: mLength)를, 글로벌 변수들은 "g" 접두사(예: gWindowCount)를 가질 수 있습니다. 글로벌 변수들은 :: 글로벌 scope로 표시할 수 있습니다.
    예: size, isDeletable, newFont
     
  5. Constants (const, enum)
    첫 글자는 대문자입니다; 모든 새로운 단어 역시 대문자로 시작해야 합니다. 공통 scope에서 정의된 enums의 이름은 달라야 합니다. 그래서 여기서는 접두사를 사용해도 됩니다.
    예: Underscore, DefaultSize
     _
  6. Function 이름
    첫 글자는 대문자입니다; 모든 새로운 단어 역시 대문자로 시작해야 합니다.
    E.g.: Print, SetPort
     
  7. Method 이름
    첫 글자는 대문자입니다; 모든 새로운 단어 역시 대문자로 시작해야 합니다.
    되도록이면 이름에 동사를 사용하거나, 동사를 포함하는 구조를 사용하십시오. (예. verb + object) 예: Move, FindLast, AppendName, DeleteIcon.
    동사 Set, Get, Has는 속성 정보를 세트하거나 가져오는 method 이름으로 사용해야 합니다. 예. SetSize, GetSize, IsEmpty, HasIcon
    이름을 지을 때에는 'To' 접두사로 시작해야 합니다. 그리고 동사는 없어야 합니다. 예: ToString, ToDouble
     
  8. Parameter 이름
    입력, 출력 또는 입/출력 파라미터들의 경우 in/out/io 접두사를 사용해도 됩니다. (예: inString, outText, ioLength)
     
    약어는 대문자 규칙에서 예외입니다. 예. ISO_Standard, 여기서 ISO는 모두 대문자로 작성합니다.  

3. 표현식
 


4. 제어 흐름 구문
 

  1. if-else
     
    • if (condition)
          statement;
       
    • if (condition) {
          // body
      }
       
    • if (condition)
          statement1;
      else
          statement2;
       
    • if (condition) {
          // body
      } else {
          // body
      }
       
    • if (condition1) {
          // body
      } else if (condition2) {
          // body
      } else if (condition3) {
          // body
      } else {
          // body
      }
       
    내장된 "단순" ({ } 없는) conditions를 작성하지 마십시오; 가장 안쪽의 condition만 "단순"할 수 있습니다. (심지어 거기에 어떤 else가 없더라도):
     
    if (condition1) {
        if (condition2)
            statement1;
        else
            statement2;
    }
     
    만약 조건적 if 구문 안에 complex branch가 있다면 모든 것이 complex 이어야 합니다.
    condition은 어떤 할당도 포함해서는 안 됩니다.
  2. switch
     
    switch (key) {
        case Value1:
            // body
            break;

        case Value2:
            // body
            // no break

        case Value3:
        case Value4:
            // body
            break;

        case Value5:
            }
                // local 변수를 갖고 있는 body
            }
            break;

        default:
            // body
            break;
    }
     

    default branch를 필수적으로 갖고 있어야 합니다 (오류 처리!) default 는 항상 마지막에 있어야 합니다.
    만약 비어 있지 않은 case branch의 body를 실행한 후에 다음 case branch에서 continue하게 되면 "no break" 코멘트를 추가하는 것은 필수입니다.
    무조건적인 return throw 구문 뒤에는 break를 추가하면 안 됩니다.

    짧은-body 유사한 case branch의 경우(예. conversion), 당신은 좀 더 가독성 있는 콤팩트한 switch 형태를 사용할 수 있습니다:

        switch (code) {
            case 'I':  return 1;
            case 'R':  return 2;
            case 'B':  return 3;
            case '\n': return 4;
     
            default:   return 0;
        }

  3. for, while, do-while
     
    • for (Int32 i = 0; i < 100; i++)
          statement;
       
    • for (Int32 i = 0; i < 100; i++) {
          // body
      }
       
    • for (Int32 i = 0; i < 100; i++)
          ;    // 빈 body
       
    내장된 "단순" ({ } 없는, 예제 1) 루프를 작성하지 마십시오. 가장 안쪽의 루프만 "단순"할 수 있습니다.

    만약 loop 변수가 loop 안에서만 필요하다면, 위의 예제들처럼 for loop의 header에서 정의할 수 있습니다

    만약 for loop의 head로부터 어떤 구문이라도 빠뜨린다면, '(', ';', or ')' 문자 사이에 공백을 두어야 합니다. 예: for ( ; ; )
     

    •  
    • while (condition)
          statement;
       
    • while (condition) {
          // body
      }
       
    내장된 "단순" ({ } 없는, 예제 1) 루프를 작성하지 마십시오. 가장 안쪽의 루프만 "단순"할 수 있습니다.
    condition 안에 할당을 사용하지 마십시오.
     
    •  
    • do
          statement;
      while (condition);
       
    • do {
          // body
      } while (condition);
       
    내장된 "단순" ({ } 없는, 예제 1) 루프를 작성하지 마십시오. 가장 안쪽의 루프만 "단순"할 수 있습니다.
    condition 안에 할당을 사용하지 마십시오.
     
  4. try-catch-throw
     
    try {
        // body
    }
    catch (Type1 exception) {
        // body
    }
    catch (Type2) {
        // body
    }
    catch (...) {
        // body
        throw;    // rethrow
    }
     
    만약 어떤 문제도 발생하지 않는다면 catch 가 허용하는 type 및 parameter 이름을 작성하는 방법은 다음을 권장합니다:

        catch (const Type& exception)  또는
        catch (const Type& e)          또는
        catch (const Type&)

    (만약 Type이 basic type이 아닌 경우)


5. 변수 선언
 


6. 함수
 

  1. 선언
     
    • return 값의 type과 함수 이름 사이에 1 (또는 첫 번째 공백 거리가 1개만 제공된 경우 2개) 탭을 배치하십시오. 또한 파라미터 리스트의 열린 괄호 앞에 공백을 두고 각 파라미터 구분 콤마 뒤에 공백을 남겨 두십시오. 예:
       
      void    DrawPixel (Int32 x, Int32 y);
      short   GetVersion (void);
       
      또한 type과 파라미터의 이름 사이에 공백을 두십시오.
      만약 당신이 2개 이상의 변형된 키워드를 갖고 있다면, 함수 이름은 새로운 라인으로 이동할 수 있습니다:
       
      static int cdecl
      SetPointer (char* p);
       
    • 만약 default 파라미터를 갖고 있다면, '=' 앞뒤에 공백을 두십시오:
       
      void    PrintNumber (Int32 n, short radix = 10);
       
    • 만약 여러 파라미터들과 함수 선언을 한 라인에 넣을 수 없다면, 모든 파라미터는 각자 새로운 라인에 두고 name과 type은 정렬해야 합니다. 예:
       
      String  ConcatenateName (const String* part1,
                               const String* part2,
                               const String* part3,
                               const String* part4,
                               bool          appendNewLine = false);
       
    • 가능할 때마다 const를 사용하십시오.
       
    • 만약 함수가 어떤 파라미터도 갖고 있지 않은 경우 항상 void를 쓰십시오. 예외: 기본 생성자, 소멸자, 정의에 의한 파라미터들을 갖고 있지 않은 연산자들.
       
    • 레퍼런스로 값을 뒤로 전달하거나 변수를 초기화하는 데 사용하지 마십시오. 대신 포인터를 사용하십시오. 반대로 레퍼런스를 값 또는 객체 수정을 위해 사용할 수 있습니다. (입/출력 파라미터)
       
    • bool 파라미터는 가장 단순한 case 안에만 전달하십시오. 대신 enum을 사용하십시오.
       
    • 오버로딩하는 연산자는 natural conversion에서만 사용하십시오. 연산자는 operator 키워드 바로 뒤에 따라와야 합니다.
       
    • 명명 규칙: => Function 이름을 보십시오.
       
  2. 구현
     
    • 스타일:
       
      void    PrintNumber (Int32 n, short radix)
      {
          // body
      }
       
    • 함수 헤더는 함수 선언에 대해서 위에서 설명한 규칙을 따라야 합니다.
       
    • 함수의 body를 작성할 때 오른쪽으로 1개의 탭만큼 들여쓰기 해야 합니다.
       
    • 함수의 닫는 '}' 전에 2개의 빈 라인을 남겨두어야 합니다.
       
    • 라인당 1개의 구문을 작성해야 합니다.
       
    • 함수는 80 라인을 초과하지 마십시오.
       
    • 너무 콤팩트한 구조, 매우 복잡한 표현식에 마법의 C 트릭을 쓰지 않도록 하십시오.
       
    • 조건적 표현식은 최대 3 레벨 깊이까지만 유지하십시오; 만약 더 깊이 들어가게 되면 분리된 (inline) 함수로 코드를 분리하십시오.
       
    • 많은 (2개 이상의) return 구문을 사용하지 않도록 하십시오. 또한 함수의 시작 부분에 조건적인 return을 제외하면, 함수 body의 중간에서 return 구문을 두지 않도록 하십시오.
       
    • 함수 body의 시작 부분에 들어오는 파라미터(전제조건)들을 확인하기 위해 Assert를 사용하십시오.
       
    • local (또는 static) 변수의 선언은 변수 선언 챕터에서 논의했습니다.
       

7. Type 정의
 

  1. struct
     
    struct Vector {
        double x;    // Vector의 x 좌표
        double y;    // Vector의 y 좌표
        double z;    // Vector의 z 좌표

        bool   isNormalized;    // 만약 true이면, Vector는 Normalized입니다
    };
     

    struct에 대하여 default public 키워드를 작성하지 마십시오. 그리고 protected 또는 private 섹션을 사용하지 마십시오. 만약 당신이 그러한 섹션들이 필요하다면 대신 class를 작성하십시오. 마찬가지로 static data member와 상속을 사용하지 마십시오.
    Data member 선언에 대한 다른 규칙들은 class에 대한 Data member 선언 챕터에서 찾을 수 있는 것과 같습니다.

    만약 생성자가 필요하다면, data member와 생성자 사이에 빈 라인을 두십시오. 예:
     

    struct Point {
        Int32 x;    // Point의 x 좌표
        Int32 y;    // Point의 y 좌표

        Point ();
        Point (Int32 xc, Int32 yc);
    };
     

    Structure는 C 언어에서 익숙한 의미로만 사용할 수 있습니다. Structure는 data member 옆에 생성자와 생성자 같은 (Set) method들을 가질 수도 있습니다.
    Method 선언에 대한 다른 규칙들은 class에 대한 Method 선언 챕터에서 찾을 수 있는 것과 같습니다.
    Embedding structures into classes에 대한 정보는 Embedded classes 및 structures 챕터를 보십시오.
     
  2. union
     
    Union은 srtuct와 동일한 규칙을 따라야 합니다.
    공간을 최적화할 때에만 union을 사용하십시오.
     
  3. enum
     
    enum Status { OK, Error };    // 많은 method의 return 값

    enum DataType {    // interpreter의 가능한 데이터 type
        Int,
        Real,
        Bool,
        String,
        Object,

        Void
    };
     

    값 할당의 경우 '=' 기호와 값들을 모두 정렬하십시오:
     
    enum Token    // tokenizer 상수
    {
        Integer   = 25,
        Real      = 48,
        Character = 701
    };
     
    부디 역할을 설명하는 comment를 enum에 추가하십시오.

    각 플랫폼에서 enum의 물리적 크기가 다를 수 있음을 유념하십시오. 그러므로, 가량 enum을 포함하는 structure의 길이가 달라질 수 있습니다. 만약 이것을 바이너리 형태로 작성하면 문제가 발생할 수 있습니다. 이것을 피하려면 컴파일하기 전에 enum의 크기를 int의 크기로 바꾸십시오.
    만약 enum이 파일에 기록된다면 enumerator에게 값을 부여하십시오.
     

  4. typedef
     
    typedef Int32 Offset;

    typedef unsigned Int32  Index;

    typedef VectorFS<double, 3>  Vector3D;
     

    typedef 포인터를 피하십시오.
     
     

8. Class 정의
 

  1. Base structure
    1. Visibility - 개발자의 관점(private, protected, public), 그리고 사용자의 관점(public, protected, private)이 허용됩니다. 각 섹션에는 하나의 섹션만 있을 수 있습니다. 예:
       
      class Vector {
      private:
          // private part

      protected:
          // protected part

      public:
          // public part
      };
       

      만약 class의 헤더가 여러 라인에 걸쳐져 있으면 class body의 열리는 bracket '{'은 새로운 라인으로 가야 합니다. (상속을 보십시오) 드문 경우, public (또는 protected) 파트가 선언들을 포함합니다. (주로 사용자 type, 예. enum) 이 선언들은 private (또는 protected) 섹션에서 사용됩니다. 이 선언들은 class의 시작 부분에 보여야 합니다:
       
      class Vector {
      public:    // predefinitions
          typedef double Coord;
          // private (protected) part에서도 사용하는 다른 public 정의

      private:
          // private part

      protected:
          // protected part

      public:
          // public part
      };

    2. Template classes
       
      template <class Type, int BufferSize = 16>
      class Array {
          // body
      };

      Template 인수는 함수의 인수와 비슷하게 취급되어야 합니다.
       

    3. 상속
       
      class Derived: public Base1,
                     public Base2,
                     protected Base3,
                     private Base4
      {
          // body
      };
       
      상속 시에는 항상 default private 키워드를 작성해야 합니다.
      가상 상속을 위해 public virtual Base ordering을 사용하십시오.
      단일 상속의 경우 열리는 bracket '{'은 헤더 라인의 끝으로 가야 합니다. (class ...)
    4. 명명 규칙
  2. Body
    1. private 섹션 - 일반적인 레이아웃, 권장사항, 규칙
       
      class String {
      private:
              // definitions

              typedef short Symbol;

          enum InternalStatus { OK, Error };
       
              // static (class) data members

          static const Int32  MaxSize;    // 짧은 설명
          static char        defChar;    // 짧은 설명

              // normal (instance) data members

          char*  str;    // 짧은 설명
          Int32   size;   // 짧은 설명

                      // static (class) functions

           static unsigned char*  ToPascalString (const char* cStr);

              // normal (instance) member functions (methods)

              void          Clear      (void);
          Int32          SkipSign   (Int32 from) const;
          inline Int32   SkipDigits (Int32 from) const;
          virtual void  Print      (void) const;
       
      protected:
          // protected part

      public:
          // public part
      };
       

      더 나은 가독성을 위해 default private 키워드가 항상 class의 시작 부분에 나타나야 합니다.

      Disabled methods (예. constructors)에 // disabled comment를 붙이고 일반 method 리스트의 시작 부분에 두십시오. 예:
       

      String (const String& source);               // disabled
      String& operator= (const String& source);    // disabled
       
      만약 friend classes 또는 functions를 사용한다면, 모든 private data와 methods에 대해 알 필요가 있을 경우 private 섹션의 시작 부분에 나와야 합니다.:

          private:
              friend class Menu;    // must be friend because ...

      friend 클래스가 약간의 private methods 또는 data만 필요하다면, 그것들 바로 옆에 friend 선언을 두십시오. 그렇게 하면 사용자의 friend class가 우리의 class로부터 사용하는 내용을 볼 수 있을 것입니다. (이것은 우리 class에 접근할 수 있는 그러한 class들의 즉각적인 가시성을 파괴합니다)
      당신은 friend 선언 뒤에 짧은 comment로 반드시 설명해야 합니다. (동일한 라인 상에)
      (이 규칙의 예외는 그것들의 public friend 연산자 함수들이 normal method가 아니어서 컴파일러가 연산자의 대칭성을 보장합니다. 예. Vector class의 operator+ (const Vector& v1, const Vector& v2)friend 함수).
    2. protected 섹션 - 일반적인 레이아웃, 권장사항, 규칙
       
      protected 섹션에 대한 특별한 권장사항과 규칙은 없습니다. 예외적으로 다음 권장사항이 있습니다:
      protected data members를 가능한 적게 사용하십시오; 대신 protected (하다못해 inline) methods를 사용하십시오.
       
    3. public 섹션 - 일반적인 레이아웃, 권장사항, 규칙
       
      class String {
      private:
          // private part

      protected:
          // protected part

      public:
              // definitions

              typedef char* CStr;

          enum SymbolType { ASCII, WideChar };

              // 선택적인 예외 classes의 선언 (types)
       
              // static const (class) data members

          static const char  NewLine = '\n';     // 짧은 설명
          static const char  MaxLength = 256;    // 짧은 설명

                      // static (class) functions

           static char*  GetClassName (void);

              // Constructors

          String ();
          String (const char* cStr);
          String (const String& source);
         ~String ();
          String&  operator= (const char* cStr);
          String&  operator= (const String& source);

              // normal (instance) member functions (methods)

          void          Delete     (Int32 idx);
          void          Delete     (Int32 from, Int32 range);
          void          DeleteLast (void);

          inline void   Clear      (void);

          void          Replace    (Int32 from, Int32 range, char ch);

          inline charoperator[] (Int32 idx);
          inline char   operator[] (Int32 idx) const;

          virtual Int32  Read       (IChannel& ic);
      };

      Class에서 public non-const data members를 사용하지 마십시오.

       
    4. Data member 선언
       
      • Data members(라인당 1개)는 서로 아래에 나타나야 하며, 각 member 뒤에 짧은 설명을 붙여야 합니다.
      • 만약 data member가 많으면 정렬하십시오. type과 name 사이에는 최소한 2개의 공백을 두십시오. 예:
         
        char*          str;    // 할당된 문자열을 가리킴 (만약 없으면 NULL)
        UInt32  size;   // 문자열의 크기
         
      • 만약 data member가 많으면, 논리적 그룹 간에 빈 라인을 두십시오.
      • 만약 member가 많은 수정된 키워드(static, const, mutable)를 갖고 있다면, 이것이 올바른 순서입니다:
        • static const
        • mutable const
    5. Method 선언
       
      • method 선언 뒤에 comments를 두어서는 안 됩니다. (publicprotected methods는 문서에서 설명되어야 합니다. 구현 내 private methods는 class를 쉽게 이해할 수 있도록 만들어야 함)
      • 만약 methods를 그룹화하면, methods의 이름을 정렬하고 되도록 return type과 name 사이에 탭 1개(혹은 공백 하나 공간만 나오면 2개)를 두십시오. 또한 이름이 너무 긴 경우를 제외하고는 파라미터들을 정렬하십시오. 예:

                 /* IO methods */
         

        virtual Int32  Read     (IChannel& ic);
        Int32          ReadQuotedString (IChannel& ic, bool skipLeadingWhiteSpace = true);
        Int32          ReadLine (IChannel& ic);
        virtual Int32  Write    (OChannel& oc) const;
         
        method의 이름과 파라미터 리스트의 열린 괄호 사이에 최소한 하나의 공백을 두십시오.
      • 만약 method가 많은 파라미터를 갖고 있으면, 선언을 1개 라인에 맞추지 않습니다. 모든 파라미터는 새로운 라인에 나타나야 합니다. 파라미터의 type과 name은 정렬되어야 합니다. 예:
         
        virtual Vector3D  ComputePosition (const Matrix& rot,
                                           Mode          mode,
                                           double        x,
                                           double        y,
                                           double        z) const;

        이 경우, class가 더 눈에 잘 띄게 될 거라고 느끼면 method 선언 뒤에 빈 라인을 둘 수 있습니다.

         
      • template method 선언의 스타일:
         
        template <class U, class V>
        inline Pair&  operator= (const Pair<U, V>& p);
      • 만약 methods를 논리적으로 그룹화한다면, method 그룹들 앞에 간략한 그룹 설명이나 빈 라인을 둘 수 있습니다. 그룹 설명 앞뒤로 빈 라인을 두십시오. 그리고 탭 1개만큼 들여쓰기 하십시오. 예:
         
            /* 비교 methods */
         
        bool         operator== (const char* cStr) const;
        bool         operator!= (const char* cStr) const;

            /* 변환 methods */

        const char*  ToCStr (void) const;
        const char*  ToCStr (Int32 from, Int32 range) const;

      • (operator= 같은) 생성자 같은 methods를 생성자 그룹 안에 두십시오. 이 methods에 대한 다른 예제는 다음과 같습니다. (권장 이름을 갖고 있음):
         
        • static String*   NewInstance (void);  - 새로운 String을 생성하지만 당신은 그것의 주소를 취할 수 있습니다. (생성자와 반대)
        • String&  Assign (const char* charPtr, Int32 charCount);  - operator= 와 같음, 그러나 많은 파라미터들을 가질 수 있습니다.
        • virtual String*  Clone (void) const - String을 복사합니다.
         
      • 빈 - void가 아닌 - () 파라미터 리스트를 가진 default 생성자와 소멸자를 작성하십시오. 그렇게 하면 쉽게 찾을 수 있고, 정의에 의해 어떠한 파라미터도 갖고 있지 않다는 것을 강조할 수 있습니다. (당신은 파라미터 없이 동일한 방식으로 operator 함수들을 작성해야 합니다) 다른 모든 methods와 함수들의 경우, 파라미터가 없으면 당신은 항상 void 키워드를 작성해야 합니다.
         
      • 항상 생성자들의 이름을 정렬해야 합니다. 심지어 explicit 또는 inline 키워드를 사용하더라도 그러합니다. 예:
         
                 String ();
        explicit String (char c);
                 String (const char* cStr);
        inline   String (const char* charPtr, Int32 charCount);
        explicit String (unsigned char* pStr);
                 String (const String& source);
         
        만약 당신이 자동 변환을 원치 않는다면, 1개의 파라미터를 갖는 생성자 앞에 explicit 키워드를 항상 두어야 합니다. (복사 생성자는 예외)
         
      • 복사 생성자할당 연산자의 필수 파라미터 이름은 source입니다:
         
        String (const String& source);
        String&  operator= (const String& source);
         
        특수한 경우를 제외하고는 항상 const를 작성해야 합니다.
         
      • 당신은 그러한 모든 methods 앞에 virtual 키워드를 작성해서 잘 찾을 수 있게 해야 합니다. 설령 method가 이 속성을 상속하더라도 말입니다.
         
      • abstract virtual methods의 스타일:
         
        virtual void  Draw (void) const = 0;
         
        = 기호 앞뒤에 공백을 둡니다.
         
      • 많은 수식어 키워드의 경우 (static, virtual, inline, explicit, friend) 순서는 다음과 같습니다:
        • static inline
        • virtual inline
        • explicit inline
        • friend inline
         
      • 당신은 class 선언 안에 method 구현을 작성하지 마십시오. (embedded classes의 짧은 methods는 제외)
         
      • 다른 모든 점에서 (파라미터의 배치, default 파라미터 등) methods의 선언은 함수 선언의 규칙을 따릅니다.
       
    6. Embedded classes 및 structures
       
      embedded (유틸리티) classes와 structures는 class의 선언 파트 안에 두어야 합니다. 그리고 1개의 탭만큼 들여쓰기 해야 합니다. 예:
       
      class Vector {
      private:
              // 정의

          struct RealPair {
              double  r1;    // 1번째 값
              double  r2;    // 2번째 값
       
              RealPair (): r1 (0), r2 (0) {}
              RealPair (double val1, double val2) r1 (val1), r2 (val2) {}
          };

      ...

      };
       

      이러한 작은 유틸리티 classes의 경우, class 안에 method 구현을 둘 수 있습니다.
      큰 embedded classes 작성을 피하십시오. 대신 잘 숨겨진 namespace를 만드십시오. (예. String class with the 이름 StringPrivate을 가진 String class에 대하여) 그리고 거기에 embedded class를 두십시오.
       
    7. 명명 규칙  

9. 클래스 구현
 

  1. 정적 (클래스) 멤버
     
    Int32          Array::defaultInitialCapacity = 100;
    const Int32    Array::MaxInitialCapacity     = 1000000000;
    const double  Array::DefaultAllocationUnit  = 100.5;
     
    data member 이름, '=' 기호, initializer 값을 정렬하도록 하십시오.
    소스 (.cpp) 파일의 시작 부분에 static data members를 선언하십시오.
  2. 메서드
     
    • method 구현 함수들 간에 2개의 빈 라인을 두십시오. 헤더 파일의 class 끝 뒤에 public inline 함수를 3개의 빈 줄로 두십시오.
       
    • 구현 파일의 methods의 순서는 class 선언에서의 순서와 같아야 합니다. 먼저 생성자를 작성하고, 다른 public methods를 작성합니다. (class 선언에 나온대로) public methods 뒤에 private methods를 구현하는 것이 실용적입니다. 그리고 protected methods는 가장 논리적으로 맞는 위치에 둡니다.
       
    • 생성자의 스타일
       
      Array::Array (Int32 initialCapacity, Int32 maxSize):
          base1 (initialCapacity),
          base2 (0, 25),
          member1 ("", 40),
          member2 (1)
      {
          // body
      }
       
      ancestor classes의 initializers와 data member를 정렬해야 합니다. ancestor classes의 initializers가 먼저 와야 합니다. 그리고 나서 data members는 상속한 순서대로, 그리고 data member를 선언한 순서대로 나타나야 합니다. data members는 실용성이 있다면 body에서 초기화될 수도 있습니다.
    • Template methods의 스타일:
       
      template <class Type>
      inline Int32  Array<Type>::GetSize (void) const
      {
          // body
      }
       
    • method 선언 챕터는 method 헤더의 스타일을 정의합니다.
       
    • 당신은 data members와 함수들에 접근할 때 this가 독특한 역할을 갖고 있을 때에만 this 포인터를 사용해야 합니다. (예. this->x) 이것을 절대로 static members를 위해 사용하지 마십시오. 또한 ancestor class(es)의 members에 접근할 때 사용할 필요가 없습니다. class의 scope의 바깥에 있는 이름에 접근할 때, 당신은 scope를 사용해야 합니다. 예. (아마도 OS) Sleep () 함수를 이런 식으로 호출하십시오. ::Sleep ()
       
    • 가능하면 data members와 동일한 이름을 가진 method 인자들을 사용하지 마십시오. 만약 다른 논리적 이름을 찾을 수 없다면, data members에 접근할 때에는 꼭 this 포인터를 사용하십시오.
       
    • 할당 연산자를 작성하기 위한 추천하는 스타일 (operator=):
       
      Point2D&  Point2D::operator= (const Point2D& source)
      {
          // source가 NULL인지 테스트함

          if (this != &source) {
              BaseClass1::operator= (source);    // 만약 Point2D가 BaseClass1이라는 base class를 갖고 있다면
              BaseClass2::operator= (source);    // 만약 Point2D가 BaseClass2이라는 base class를 갖고 있다면

              x = source.x;    // 자신의 data member
              y = source.y;    // 자신의 data member
          }

          return *this;
      }
       

    • 다른 점에서 method 구현은 함수 구현의 규칙을 따라야 합니다.
     
     

10. 네임스페이스
 

namespace CLib {
    void*  MemCopy (void* destPtr, const void* srcPtr, unsigned Int32 count);
    void*  MemMove (void* destPtr, const void* srcPtr, unsigned Int32 count);
    void*  MemSet  (void* destPtr, int filler, unsigned Int32 count);
}
 
당신은 단순한 함수들, type 정의(typedef), 상수만 포함하는 namespace의 body를 1개 탭만큼 들여쓰기 해야 합니다.
만약 당신이 namespace 안에서 (더 큰) class를 정의한다면, 들여쓰기가 항상 실용적이지는 않습니다. 이 경우 다음 스타일을 사용하십시오:
 
namespace Math  {
 
class Vector {
    // body
};

}   // namespace Math
 

이 경우, 당신은 namespace의 끝에 closing comment를 추가해야 합니다.

함수의 구현에 대하여 namespace를 다시 열지 마십시오. 예. MemCopy 함수의 구현을 namespace 밖이면서 그 위에 작성하십시오:
 

void*  CLib::MemCopy (void* destPtr, const void* srcPtr, unsigned Int32 count)
{
    // body
}
 
당신이 우연히 MemCopy 대신 MemCpy를 작성했다면 컴파일은 이 경우 warning을 줄 것입니다. 그렇지 않으면 컴파일러가 새로운 "bad" MemCpy 함수를 namespace 안에 둘 것입니다.

이 오류는 class method 구현에서 매우 드물기 때문에 (제공된 method가 class 안에서 앞 부분에 선언되어야 함) 이 경우 당신은 namespace 밖에서 구현하고 namespace를 다시 열거나, using 지시어를 사용할 수 있습니다.

main () 함수를 제외하고 모든 것은 namespace에 들어가야 합니다. (기껏 해야 당신은 주어진 namespace를 열어야 함) 이것은 특히 외부 라이브러리를 사용하는 경우 잠재적인 이름 충돌을 피하기 위해 중요합니다.

구현 파일에서 주로 using namespace X 지시어를 사용하십시오. 실제로 필요한 경우에만 헤더 안에 나타나야 합니다. (예. 만약 열린 namespace 안에 당신의 base module을 갖고 싶을 경우) 헤더 안에 "분리된" using 선언을 사용하지 마십시오. 이 선언들은 헤더를 #include 하는 파일들에 의해 무심코 상속될 수 있습니다.

using 지시어를 사용하는 주된 이유는 주어진 이름(들)이 꽤 자주 나타나는 파일에만 넣기 때문이다. 예. 왜냐하면 그것은 그 영역의 기본 단위(type, constant 등)이기 때문입니다. 약어 두어 개만 사용하는 경우에만 using을 두지 마십시오. 그렇지 않으면 그냥 더 작은 scope 안에 넣으십시오. 예. method 안쪽. 기본적으로 우리는 namespace를 사용하여 모듈과 거기에 선언된 이름을 분리하고 구별하여 수량과 scope 모두에서 using 지시어의 숫자를 제한합니다.

정말로 필요한 곳에서만 namespace alias를 사용하십시오.


11. 전처리기 지시어
 

#if defined XXX

// body

#else

// body

#endif

 
조건부 전처리기 구문 안에 "일반" C++ 코드를 들여쓰기 하지 마십시오; 그러나 다른 전처리기 구문에는 1개의 탭만큼 들여쓰기 하십시오.
2개 화면보다 더 긴 structures 안에 comments를 두십시오. 그러면 일치하는 지시어들을 쉽게 찾을 수 있을 것입니다.
짧은 #ifdef#ifndef 지시어를 사용하지 마십시오.

#include와 파일명 사이에 공백을 두십시오:
 

#include "Array.hpp"
 
두어 가지 권장사항:  
 

12. 코멘트 작성하기
 


13. 파일 structure