Copyright IBM Corp. 1987, 2005. All Rights Reserved.

"Rational" 및 Rational의 제품은 Rational Software Corporation의 상표입니다. 기타 회사 및 기타 회사의 제품을 나타내는 표장은 해당 회사가 소유하는 상표이며 참고용으로만 제공됩니다.

이 문서는 Calypso Software Inc.(캐나다, B.C. 벤쿠버 소재)의 Luan Doan-Minh이 Rational Software Corp.용으로 준비한 문서입니다.


목차

소개

기본 원칙
가정
가이드라인 분류
기본 가이드라인

코드 조직 및 스타일

코드 구조
코드 스타일

주석

이름 지정

일반
이름 공간
클래스
함수
오브젝트 및 함수 매개변수
예외
기타

선언

이름 공간
클래스
함수
유형
상수 및 오브젝트

표현식 및 명령문

표현식
명령문

특수 주제

메모리 관리
오류 처리 및 예외

이식성

경로 이름
데이터 표시
유형 변환

재사용

컴파일 문제

가이드라인 요약

요구사항 또는 제한조건
권장사항

참고 문헌


1장

소개

대규모 소프트웨어 프로젝트는 일반적으로 그에 상응하는 대규모의 여러 개발자 팀이 함께 수행합니다. 대규모 팀에 의해 생성된 코드가 프로젝트 전체에서 측정 가능한 품질을 유지하기 위해서는 코드를 표준에 따라 작성하고 표준에 따라 판단해야 합니다. 따라서 대규모 프로젝트 팀에서는 프로그래밍 표준 또는 특정 가이드라인을 설정해야 합니다.

이 밖에도 프로그래밍 표준을 사용함으로써 얻을 수 있는 이점은 다음과 같습니다.

이 문서에서는 표준의 기반으로 사용할 수 있는 C++ 프로그래밍 규칙, 가이드라인 및 힌트(일반적으로 지침이라고 함)를 제공합니다. 이 문서는 대규모 프로젝트 팀에서 작업하는 소프트웨어 엔지니어를 위한 문서입니다.

경우에 따라 프로그래밍과 디자인의 경계를 구분하는 것이 어렵기도 하지만 현재 버전은 프로그래밍에 주로 초점이 맞추어져 있으며 디자인 지침은 이후에 추가될 예정입니다.

제공되는 가이드라인은 C++ 개발의 다음 측면에 대해 다룹니다.

이 가이드라인은 다양한 산업 지식을 기반으로 수집되었습니다 (소스(작성자 및 참조)는 참고 문헌 참조). 이 내용은 다음을 기반으로 합니다.

대부분의 가이드라인은 첫 번째 카테고리를 기반으로 하지만 두 번째, 세 번째 카테고리도 상당 부분 적용됩니다. 또한 프로그래밍은 매우 주관적인 활동이며 누구나 인정하는 "가장 좋은" 또는 "우수한" 코딩 방법은 없으므로 마지막 카테고리를 기반으로 하기도 합니다.

기본 원칙

대부분의 규칙 및 가이드라인의 기본 목적은 명확하고 쉽게 이해할 수 있는 C++ 소스 코드입니다. 명확하고 쉽게 이해할 수 있는 소스 코드는 소프트웨어의 신뢰성 및 유지보수성을 위한 중요한 기여 요소입니다. 명확하고 쉽게 이해할 수 있는 코드란 다음과 같은 세 가지 간단한 기본 원칙([Kruchten, 94])으로 규정할 수 있습니다.

균일성(Minimal Surprise) - 소스 코드에는 주로 쓰기보다 읽기, 특히 세부화 작업이 수행됩니다. 이상적으로, 코드는 수행하는 내용을 영어로 설명하듯이 읽을 수 있되 실행 가능하다는 이점이 있을 뿐이라고 보는 것이 가장 좋습니다. 프로그램은 컴퓨터보다는 사람을 위해 작성되는 것입니다. 코드 읽기는 복잡한 정신적 프로세스지만 균일성(Minimal Surprise)을 유지함으로써 쉬운 작업이 될 수도 있습니다. 전체 프로젝트에서 통일된 스타일을 유지하려면 소프트웨어 개발자 팀이 프로그래밍 표준에 동의해야 하지만, 이를 일종의 처벌이나 창의성과 생산성에 대한 장애물로 인식해서는 안됩니다.

단일 유지보수 지점 - 가능한 경우 디자인 결정은 소스 내 한 지점에서만 표현되어야 하며 대부분의 해당 결과는 이 지점에서 프로그래밍 방식으로 파생되어야 합니다. 이 원칙을 위반하는 경우 유지보수성 및 신뢰성은 물론 이해용이성에도 나쁜 영향을 주게 됩니다.

최소 혼잡 - 마지막으로, 읽기 쉽게 작성하는 중요한 요인으로 최소 혼잡 원칙을 적용해야 합니다. 즉, 막대, 상자 및 정보 내용이 거의 없거나 소프트웨어의 목적을 이해하는 데 불필요한 정보를 제공하는 텍스트와 같은 시각적 "혼잡"으로 소스 코드를 어지럽게 만들어서는 안됩니다.

이 문서에서 설명하는 가이드라인의 목적은 지나친 제한이 아니라 언어 기능에 대한 안전하고 올바른 사용을 위한 지침을 제공하는 것입니다. 즉, 좋은 소프트웨어를 만들기 위해서는 다음을 따라야 합니다.

가정

이 가이드라인에서는 다음 몇 가지를 가정합니다.

가이드라인 분류

모든 가이드라인의 중요성이 같지는 않으며 중요성에 따라 다음과 같이 분류됩니다.

팁:팁 아이콘

위의 기호로 식별되는 가이드라인은 따를 수도 있고 그냥 무시할 수도 있는 간단한 조언 수준의 팁입니다.

권장사항: 오케이 표시 손 모양 아이콘

위의 기호로 식별되는 가이드라인은 일반적으로 보다 기술적인 배경을 기반으로 합니다. 이를 통해 캡슐화, 연결, 결합, 이식성 또는 재사용 가능성은 물론 일부 구현에서는 성능까지 영향을 받을 수 있습니다. 권장사항은 특별한 이유가 없는 한 수행해야 합니다.

요구사항 또는 제한사항: 검지 아이콘

위의 기호로 식별되는 가이드라인은 요구사항 또는 제한조건으로서, 위반하는 경우 코드가 잘못되거나 신뢰할 수 없거나 이식할 수 없게 됩니다. 요구사항 또는 제한조건은 반드시 준수해야 합니다.

기본 가이드라인

검지 아이콘상식 사용

관련 규칙 또는 가이드라인이 없거나 규칙이 명확히 적용되지 않거나 어떤 방법으로도 실패하는 경우, 상식선에서 기본 원칙을 확인하십시오. 이 규칙은 다른 모든 규칙에 우선합니다. 상식은 규칙 및 가이드라인이 있는 경우에도 필요합니다.


2장

코드 조직 및 스타일

이 장에서는 프로그램 구조 및 레이아웃에 대한 안내를 제공합니다.

코드 구조

대규모 시스템은 일반적으로 여러 개의 소규모 기능 서브시스템으로서 개발됩니다. 서브시스템은 일반적으로 여러 개의 코드 모듈로 구성됩니다. C++의 경우 모듈에는 일반적으로 단일 또는 드물게 밀접한 관계의 추상 세트가 포함됩니다. C++에서는 일반적으로 추상은 클래스로 구현됩니다. 클래스에는 독특한 두 개의 컴포넌트, 즉 클래스 성능 및 책임의 선언 또는 스펙을 제공하며 클래스 클라이언트에 표시되는 인터페이스와 선언된 스펙의 구현(클래스 정의)이 있습니다.

클래스와 마찬가지로 모듈에도 인터페이스와 구현이 있습니다. 모듈 인터페이스에는 포함된 모듈 추상에 대한 스펙(클래스 선언)이 포함되고 모듈 구현에는 추상의 실제 구현(클래스 정의)이 포함됩니다.

서브시스템 생성 시, 서브시스템은 또한 협력 그룹 또는 레이어로 구성되어 해당 종속성을 최소화하고 제어합니다.

오케이 표시 손 모양 아이콘모듈 스펙과 구현을 별도 파일에 배치

모듈 스펙은 해당 구현과 별도 파일에 배치해야 합니다. 스펙 파일은 헤더라고 합니다. 모듈 구현은 하나 이상의 구현 파일에 배치할 수 있습니다.

모듈 구현에 광범위한 인라인 기능, 공통 implementation-private 선언, 테스트 코드 또는 플랫폼 특정 코드가 포함되는 경우, 이 파트를 해당 파일로 분리하고 해당 파트의 컨텐츠에 따라 각 파일의 이름을 지정하십시오.

실행 가능 프로그램 크기가 문제인 경우, 거의 사용하지 않는 함수도 해당 개별 파일에 배치해야 합니다.

파트 파일 이름은 다음과 같은 방법으로 구성하십시오.

오케이 손 모양 아이콘헤더와 구현 파일을 구분하는 단일 파일 이름 확장자 세트 선택

파일 이름 확장자로는 일반적으로 .h, .H, .hh, .hpp가 사용되며 헤더 파일에는 .hxx, 구현에는 .c, .C, .cc, .cpp 및 .cxx가 사용됩니다. 확장자 세트를 선택하여 일관되게 사용하십시오.

예제
SymaNetwork.hh  // The extension ".hh" used to designate
                // a "SymaNetwork" module header. 
SymaNetwork.cc  // The extension ".cc" used to designate
                // a "SymaNetwork" module implementation.            
참고

C++ 표준 초안 작업 문서에서는 또한 이름 공간으로 캡슐화된 헤더에 확장자 ".ns"를 사용합니다.

오케이 표시 손 모양 아이콘모듈 스펙당 두 개 이상의 클래스 정의

반드시 필요한 경우에만 복수 클래스를 하나의 모듈에 함께 배치할 수 있으며 이 경우 클래스가 밀접하게 연관되어 있어야 합니다(예: 컨테이너와 해당 반복기). 모든 클래스가 항상 클라이언트 모듈에 표시되어야 하는 경우, 모듈의 기본 클래스와 해당 지원 클래스를 동일한 헤더 파일에 배치할 수 있습니다.

근거

모듈의 인터페이스와 이 인터페이스에 대한 다른 인터페이스의 종속성을 줄입니다.

오케이 표시 손 모양 아이콘implementation-private 선언을 모듈 스펙에 두지 않음

클래스 private 구성원을 제외하고, 모듈의 implementation-private 선언(예: 구현 유형 및 지원 클래스)은 모듈의 스펙에 표시될 수 없습니다. 이러한 선언은 여러 구현 파일에서 필요로 하지 않는 한 필요한 구현 파일에 배치해야 하며 이러한 경우 해당 선언은 보조 private 헤더 파일에 배치해야 합니다. 이 보조 private 헤더 파일은 필요에 따라 다른 구현 파일에 의해 포함될 수 있습니다.

이 방법을 통해 다음과 같은 효과를 얻을 수 있습니다.

예제
// Specification of module foo, contained in file "foo.hh"
//
class foo
{
.. declarations
};
// End of "foo.hh"
// Private declarations for module foo, contained in file
// "foo.private.hh" and used by all foo implementation files.
... private declarations
// End of "foo.private.hh"
// Module foo implementation, contained in multiple files 
// "foo.x.cc" and "foo.y.cc"
// File "foo.x.cc"
//
#include "foo.hh" // Include module's own header
#include "foo.private.hh" // Include implementation 
// required declarations.
... definitions
// End of "foo.x.cc"
// File "foo.y.cc"
//
#include "foo.hh"
#include "foo.private.hh"
... definitions
// End of "foo.y.cc"            

검지 아이콘항상 #include를 사용하여 모듈 스펙에 대한 액세스 확보

다른 모듈을 사용하는 모듈은 프리프로세서 #include 지시문을 사용하여 공급자 모듈의 스펙에 대한 가시성을 확보해야 합니다. 또한 이에 따라 모듈은 공급자 모듈 스펙의 일부를 재선언해서는 안됩니다.

파일을 포함하는 경우, "표준" 헤더에는 #include <헤더> 구문을 사용하고 나머지에는 #include "헤더" 구문을 사용하십시오.

#include 지시문 사용은 모듈의 고유한 구현 파일에도 적용됩니다. 모듈 구현에는 고유한 스펙 및 private 보조 헤더가 포함되어야 합니다("개별 파일에 모듈 스펙 및 구현 배치" 참조).

예제
// The specification of module foo in its header file 
// "foo.hh"
//
class foo
{
... declarations
};
// End of "foo.hh"

// The implementation of module foo in file "foo.cc"
//
#include "foo.hh" // The implementation includes its own
// specification
... definitions for members of foo
// End of "foo.cc"            

#include 규칙의 예외는 모듈이 공급자 모듈의 유형(클래스)을 참조용(포인터 또는 참조 유형 선언 사용)으로만 사용하거나 포함하는 경우입니다. 이러한 경우 참조용 사용 또는 포함은 #include 지시문이 아닌 전방 선언("컴파일 종속성 최소화" 참조)을 사용하여 지정합니다.

반드시 필요한 내용만 포함하십시오. 즉, 모듈 헤더에는 모듈 구현에서만 필요한 다른 헤더를 포함해서는 안됩니다.

예제
#include "a_supplier.hh"

class needed_only_by_reference;// Use a forward declaration 
                               // for a class if we only need 
                               // a pointer or a reference 
                               // access to it.
void operation_requiring_object(a_supplier required_supplier, ...);
//
// Operation requiring an actual supplier object; thus the
// supplier specification has to be #included.

void some_operation(needed_only_by_reference& a_reference, ...);
//
// Some operation needing only a reference to an object; thus
// should use a forward declaration for the supplier.            
근거

이 규칙을 통해 다음과 같은 효과를 얻을 수 있습니다.

오케이 표시 손 모양 아이콘별도 파일에 모듈 인라인 함수 정의 배치

모듈에 많은 인라인 함수가 있는 경우 해당 정의는 별도의 인라인 함수 전용 파일에 배치해야 합니다. 인라인 함수 파일은 모듈의 헤더 파일 끝에 포함되어야 합니다.

"No_Inline 조건부 컴파일 기호를 사용하여 인라인 컴파일 제거"를 참조하십시오.

근거

이 기법을 사용하면 구현 세부사항으로 모듈 헤더를 어지럽히지 않아 정리된 스펙을 유지할 수 있습니다. 또한 인라인을 컴파일하지 않을 때 코드 복제를 줄일 수 있습니다. 조건부 컴파일을 사용하는 경우, 인라인 함수가 사용 모듈 각각에 정적으로 컴파일되는 대신 단일 오브젝트 파일로 컴파일될 수 있습니다. 따라서 인라인 함수 정의가 명백하지 않은 경우 클래스 정의에 정의해서는 안됩니다.

오케이 표시 손 모양 아이콘프로그램 크기가 문제인 경우 대형 모듈을 여러 변환 단위로 분할

대형 모듈을 여러 변환 단위로 분할하면 프로그램 링크 중에 참조되지 않은 코드를 쉽게 제거할 수 있습니다. 거의 참조되지 않는 구성원 함수는 일반적으로 사용되는 함수와 별도 파일에 분리해야 합니다. 경우에 따라서는 개별 구성원 함수를 고유 파일에 배치할 수도 있습니다[Ellemtel, 1993].

근거

일부 링커는 오브젝트 파일에서 참조되지 않은 코드를 제거할 수 없습니다. 대형 모듈을 여러 파일로 분할하면 전체 오브젝트 파일의 링크를 제거하여 이러한 링커가 실행 가능 프로그램 크기를 줄일 수 있습니다[Ellemtel, 1993].

참고

모듈을 보다 작은 추상으로 분할해야 하는지 여부를 고려하는 것도 바람직합니다.

오케이 표시 손 모양 아이콘플랫폼 종속성 분리

플랫폼 독립 코드에서 플랫폼 종속 코드를 분리함으로써 포팅을 용이하게 할 수 있습니다. 플랫폼 종속 모듈은 파일 이름을 해당 플랫폼 이름으로 규정함으로써 플랫폼 종속성을 강조해야 합니다.

예제
SymaLowLevelStuff.hh         // "LowLevelStuff" 
                             // specification
SymaLowLevelStuff.SunOS54.cc // SunOS 5.4 implementation
SymaLowLevelStuff.HPUX.cc    // HP-UX implementation
SymaLowLevelStuff.AIX.cc     // AIX implementation            
참고

구조 및 유지보수 시점에서 볼 때, 플랫폼 종속성을 몇 가지 하위 레벨 서브시스템에 포함하는 것도 좋은 방법입니다.

표준 파일 컨텐츠 구조를 채택하고 일관되게 적용하십시오.

제안되는 파일 컨텐츠 구조는 다음 파트로 다음 순서에 따라 구성됩니다.

근거

위의 파일 컨텐츠 순서는 클라이언트 관련 정보를 먼저 표시하며 클래스의 public, protected 및 private 섹션 순서에 대한 근거와 일치합니다.

참고

회사 정책에 따라 저작권 정보가 파일 맨 위에 배치되어야 하는 경우도 있습니다.

오케이 표시 손 모양 아이콘반복 파일 포함으로부터 보호

각 헤더 파일에서 다음 구조를 사용하여 반복 파일 포함 및 컴파일을 방지해야 합니다.

#if !defined(module_name) // Use preprocessor symbols to
#define module_name       // protect against repeated
                          // inclusions... // Declarations go here
#include "module_name.inlines.cc" // Optional inline
                                  // inclusion goes here.
// No more declarations after inclusion of module's 
// inline functions.
#endif // End of module_name.hh            

포함 보호 기호에는 모듈 파일 이름을 사용하십시오. 기호와 모듈 이름에는 동일한 대소문자를 사용하십시오.

오케이 표시 손 모양 아이콘 "No_Inline" 조건부 컴파일 기호를 사용하여 인라인 컴파일 제거

다음 조건부 컴파일 구조를 사용하여 인라인 가능 함수의 인라인 대 아웃라인 컴파일을 제어하십시오.

// At the top of module_name.inlines.hh
#if !defined(module_name_inlines)
#define module_name_inlines

#if defined(No_Inline)
#define inline // Nullify inline keyword
#endif

... // Inline definitions go here
#endif // End of module_name.inlines.hh

// At the end of module_name.hh
//
#if !defined(No_Inline)
#include "module_name.inlines.hh"
#endif

// At the top of module_name.cc after inclusion of
// module_name.hh
//
#if defined(No_Inline)
#include "module_name.inlines.hh"
#endif            

조건부 컴파일 구조는 다중 포함 보호 구조와 유사합니다. No_Inline 기호가 정의되지 않으면 인라인 함수가 모듈 스펙과 컴파일되어 모듈 구현에서 자동으로 제외됩니다. No_Inline 기호가 정의되지 않으면 인라인 정의는 모듈 스펙에서 제외되지만 키워드 inline이 무효화되어 모듈 구현에 포함됩니다.

근거

위의 기법을 사용하면 인라인 함수가 아웃라인으로 컴파일될 때 코드 복제가 감소합니다. 조건부 컴파일을 사용함으로써, 인라인 함수의 단일 사본이 정의 모듈에 컴파일됩니다. 복제된 코드와 비교하여, 컴파일러 스위치로 아웃라인 컴파일이 지정될 때 사용되는 모든 모듈에 "정적"(내부 링크) 함수로 컴파일됩니다.

참고

조건부 컴파일을 사용하면 빌드 종속성 유지보수와 관련된 복잡도가 증가합니다. 이 복잡도는 항상 헤더 및 인라인 함수 정의를 단일 논리 유닛으로 취급하여 관리됩니다. 따라서 구현 파일은 헤더와 인라인 함수 정의 파일 모두에 종속됩니다.

코드 스타일

오케이 표시 손 모양 아이콘중첩된 명령문에 짧고 일관된 들여쓰기 스타일 사용

중첩된 명령문을 명확하게 나타내려면 일관된 들여쓰기를 사용해야 합니다. 이를 위해서는 2 - 4개 공백 들여쓰기가 가장 효과적인 것으로 알려져 있습니다. 일반적인 2개 공백 들여쓰기를 권장합니다.

복합 또는 블록 명령문 분리문자({})는 주위 명령문과 들여쓰기 레벨이 동일해야 합니다(즉, {}가 수직으로 맞춰져야 함). 블록 내 명령문은 선택한 공백 수만큼 들여쓰기해야 합니다.

switch 명령문의 case 레이블은 switch 명령문과 동일한 들여쓰기 레벨이어야 합니다. 그리고 switch 명령문 내부의 명령문은 switch 명령문과 case 레이블에서 한 레벨만큼 들여쓸 수 있습니다.

예제
if (true)
{  		// New block
  foo(); // Statement(s) within block
         // indented by 2 spaces.
}
else
{
  bar();
}
while (expression)
{
  statement();
}
switch (i)
{
case 1:
  do_something();// Statements indented by 
                 // 1 indentation level from
  break;         // the switch statement itself.
case 2:
  //...
default:
  //...
}            
근거

블록을 쉽게 인식할 수 있게 하는 것과 코드 앞에 중첩된 블록이 많아 디스플레이 모니터 또는 인쇄 페이지의 오른쪽 끝을 넘어가는 것 사이에서 절충한 방법이 두 개 공백 들여쓰기입니다.

오케이 표시 손 모양 아이콘함수 이름 또는 범위 이름에서 함수 매개변수 들여쓰기

함수 선언을 한 행에 작성할 수 없는 경우, 함수 이름과 동일한 행에 첫 번째 매개변수를 배치하고 다음 매개변수는 첫 번째 매개변수와 동일한 레벨에서 들여쓰기 하여 각각 새 행에 배치하십시오. 이러한 선언 및 들여쓰기 스타일은 함수 리턴 유형 및 이름 아래에 공백이 추가되어 가시성이 개선됩니다. 예제는 다음과 같습니다.

예제
     void foo::function_decl( some_type first_parameter, 
                              some_other_type second_parameter,
                              status_type and_subsequent);            
                              

위의 가이드라인을 준수하여 행이 바뀌거나 매개변수가 너무 많이 들여쓰인 경우 다음과 같은 함수 이름 또는 범위 이름(클래스, 이름 공간)의 모든 매개변수를 각각 개별 행에서 들여쓰십시오.

예제
     void foo::function_with_a_long_name( // function name is much less visible
               some_type first_parameter, 
               some_other_type second_parameter,
               status_type and_subsequent);            
       

또한 아래 맞추기 규칙을 참조하십시오.

오케이 표시 손 모양 아이콘표준 인쇄 용지 크기에 맞는 최대 행 길이 사용

프로그램의 최대 행 길이는 표준(레터 용지) 또는 기본 크기의 인쇄 용지에 인쇄할 때 정보가 유실되지 않는 길이여야 합니다.

참고

들여쓰기 레벨 때문에 깊게 중첩된 명령문이 너무 오른쪽에 표시되고 명령문이 오른쪽 여백을 많이 넘어가는 경우, 코드를 보다 짧고 관리하기 쉬운 함수로 분할하는 것을 고려해야 합니다.

오케이 표시 손 모양 아이콘일관된 행 접기 사용

함수 선언, 정의 및 호출의 매개변수 목록 또는 열거 선언의 열거자를 한 줄에 표시할 수 없는 경우, 해당 행을 각 목록 요소 다음에서 분할하여 각 요소를 별도 행에 배치하십시오("함수 이름 또는 범위 이름에서 함수 매개변수 들여쓰기" 참조).

예제
enum color { red, 
             orange, 
             yellow, 
             green, 
             //...
             violet
                   };            

클래스 또는 함수 템플리트 선언이 너무 긴 경우, 템플리트 인수 목록 다음에 두 행에 걸쳐 표시하십시오. 예를 들어, 표준 반복 라이브러리의 선언의 경우 다음과 같습니다[X3J16, 95].

template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n);            


3장

주석

이 장에서는 코드 내 주석 사용에 대한 지침을 제공합니다.

주석은 소스 코드를 부연 설명하는 것이 아닌 보완하기 위해 사용해야 합니다.

  • 주석은 명확하지 않은 내용을 설명하여 소스 코드를 코드를 보완하되 언어 구문 또는 시맨틱을 중복해서는 안됩니다.
  • 주석은 사용자가 배경 개념, 종속성 및 특히 복잡한 데이터 인코딩 또는 알고리즘을 이해하는 데 도움을 줘야 합니다.
  • 디자인 표준을 따르지 않은 부분, 제한된 기능의 사용 및 특별한 "요령"을 강조해야 합니다.
  • 프로그래머는 각 주석에 대해 "이 주석으로 어떤 가치가 추가되는가?"라는 질문에 쉽게 대답할 수 있어야 합니다. 일반적으로 이름을 올바르게 선택하면 주석이 필요하지 않습니다. 일부 정규 PDL(Program Design Language)과 관련되지 않은 주석은 컴파일러로 확인이 되지 않습니다. 따라서 단일 유지보수 지점 원칙에 따라, 몇 가지 선언이 더 추가되더라도 디자인 결정은 주석이 아닌 소스 코드에 표현해야 합니다.

    오케이 표시 손 모양 아이콘C 스타일 주석 대신 C++ 스타일 주석 사용

    C 스타일 "/*...*/"보다는 C++ 스타일 "//" 주석 분리문자를 사용해야 합니다.

    근거

    C++ 스타일 주석은 보다 쉽게 확인할 수 있으므로 주석 끝 분리문자 누락으로 인해 방대한 코드의 주석이 실수로 제거되는 위험을 줄일 수 있습니다.

    카운터 예제
    /* start of comment with missing end-of-comment delimiter
    do_something();
    do_something_else(); /* Comment about do_something_else */
                                  // End of comment is here ---> */
                                  // Both do_something and 
                                  // do_something_else
                                  // are accidentally commented out!
    Do_further();      

    오케이 표시 손 모양 아이콘소스 코드에 대한 주석 근접성 극대화

    주석은 설명하는 코드 근처에 동일한 들여쓰기 레벨로 배치되어야 하며 공백 주석 행을 사용하여 코드에 첨부되어야 합니다.

    복수 연속 소스 명령문에 적용되는 주석은 해당 명령문 에 배치되어 명령문을 소개해야 합니다. 마찬가지로 개별 명령문과 연관된 주석은 해당 명령문 아래에 배치되어야 합니다.

    예제
    // A pre-statements comment applicable 
    // to a number of following statements
    // 
    ...
    void function();
    //
    // A post-statement comment for 
    // the preceding statement.      

    오케이 표시 손 모양 아이콘행 끝 주석을 사용하지 않음

    주석은 배열이 잘못 될 수 있으므로 소스 구조와 동일한 행에 배치하지 않습니다. 그러나 열거 선언의 열거자와 같이 긴 선언의 요소에 대한 설명의 경우에는 이러한 주석 배치가 허용됩니다.

    오케이 표시 손 모양 아이콘주석 헤더를 사용하지 않음

    작성자, 전화번호, 작성 및 수정 날짜와 같은 정보를 포함하는 헤더는 사용하지 않습니다. 작성자 및 전화번호는 급속히 쓸모없는 정보가 되는 반면 작성 및 수정 날짜와 수정 이유는 형상 관리 도구(또는 일부 다른 양식의 버전 히스토리 파일)에서 가장 잘 관리되기 때문입니다.

    함수 및 클래스와 같은 주요 구조라도 세로줄, 닫힌 프레임 또는 상자를 사용하지 않습니다. 이러한 요소는 시각적으로 지저분할 뿐만 아니라 일관되게 유지하기도 어렵습니다. 빈 행은 긴 주석 행보다는 소스 코드의 관련 코드를 분리하는 데 사용합니다. 함수 또는 클래스 내 구조를 분리하려면 단일 빈 행을 사용합니다. 여러 함수를 서로 분리하려면 두 개의 빈 행을 사용합니다.

    프레임과 양식은 그 모양이 일정한 것처럼 보이지만 프로그래머가 코드를 문서화할 때 의역 스타일이 될 수 있으므로 주의해야 합니다[Kruchten, 94].

    오케이 표시 손 모양 아이콘비어 있는 주석 행을 사용하여 주석 단락 구분

    단락을 분리하려면 단일 주석 블록 내에서 빈 행 대신 빈 주석을 사용합니다.

    예제
    // Some explanation here needs to be continued 
    // in a subsequent paragraph.
    //
    // The empty comment line above makes it 
    // clear that this is another 
    // paragraph of the same comment block.      

    오케이 표시 손 모양 아이콘중복을 피함

    주석에서 프로그램 ID를 반복하지 않고 다른 위치의 정보를 복제하지 않습니다. 대신 정보에 포인터를 제공합니다. 그렇지 않으면 프로그램 변경으로 인해 여러 위치에서 유지보수를 수행해야 합니다. 모든 위치에서 필수 주석을 변경하지 못하면 주석이 잘못 작성되어 주석이 전혀 없는 경우보다 나쁜 결과가 발생할 수 있습니다.

    오케이 표시 손 모양 아이콘주석 대신 자체 문서화 코드 작성

    주석을 제공하는 것보다는 자체 문서화 코드를 작성하려고 합니다. 이는 임시 변수를 사용하거나 코드를 재구성하여 보다 나은 이름을 선택함으로써 가능합니다. 주석의 스타일, 구문 및 철자에 유의합니다. 전신 또는 애매한 스타일보다는 자연어 주석을 사용합니다.

    예제
    Replace:
    do
    {
      ...
    } while (string_utility.locate(ch, str) != 0); 
    // Exit search loop when found it.
    
    with:
    do
    {
      ...
      found_it = (string_utility.locate(ch, str) == 0);
    } while (!found_it);      

    오케이 표시 손 모양 아이콘클래스 및 함수 문서화

    주석에서는 자체 문서화 코드가 선호되지만 일반적으로 코드의 복잡한 파트를 설명하는 것 이상의 정보를 제공해야 합니다. 필요한 정보는 다음과 같습니다.

    근거

    코드 문서화와 선언은 클라이언트가 코드를 사용할 수 있을 정도로 충분해야 합니다. 문서화는 C++만 사용해서는 클래스, 함수, 유형 및 오브젝트의 전체 시맨틱을 완전히 표현할 수 없기 때문에 필요합니다.


    4장

    이름 지정

    이 장에서는 다양한 C++ 엔티티의 이름 선택에 대한 지침을 제공합니다.

    일반

    프로그램 엔티티(클래스, 함수, 유형, 오브젝트, 리터럴, 예외 및 이름 공간)에 올바른 이름을 지정하는 것은 쉬운 일이 아닙니다. 중간 또는 대규모 응용프로그램의 경우, 이름이 충돌하고 고유하면서도 유사한 개념을 나타내는 동의어가 부족하여 이러한 문제점은 보다 심각합니다.

    이름 지정 규칙을 사용하면 올바른 이름을 선택하는 데 필요한 정신적 수고를 줄일 수 있습니다. 이름 지정 규칙은 이러한 이점 이외에도 코드 내 일관성을 강화한다는 점에서 유용합니다. 유용한 이름 지정 규칙은 인쇄 스타일(또는 이름 작성 방법)과 이름 생성(또는 이름 선택 방법)에 대한 지침도 제공해야 합니다.

    오케이 표시 손 모양 아이콘이름 지정 규칙을 선택하여 일관되게 적용

    이름 지정 규칙은 일관된 적용이 중요할 뿐 어떤 이름 지정 규칙을 사용하는가는 그다지 중요하지 않습니다. 균일한 이름 지정이 실제 규칙보다 중요하며 이러한 균일성은 균일성(Minimal Surprise) 원칙과도 연결됩니다.

    C++ 언어는 대소문자를 구분하고 C++ 커뮤니티에서 여러 가지 고유한 이름 지정 규칙을 다양한 용도로 사용하므로 절대적인 이름 지정 일관성을 달성하는 것은 거의 불가능합니다. 따라서 UNIX 또는 Windows와 같은 호스트 환경과 프로젝트에서 사용하는 원리 라이브러리를 기반으로 프로젝트의 이름 지정 규칙을 선택함으로써 코드 일관성을 극대화하는 것이 바람직합니다.

    참고

    주의 깊은 사용자라면 이 문서의 예제가 현재 모든 가이드라인을 따르고 있지는 않다는 것을 알게 될 것입니다. 이는 해당 예제가 여러 소스에서 파생된 것인 경우도 있고 지면을 절약하기 위해서인 경우도 있습니다. 따라서 가이드라인의 형식이 완전하게 적용되지 않은 경우도 있습니다. 그러나 "표시된 예제보다는 해당 설명이 더 우선합니다".

    검지 아이콘하나 이상의 밑줄('_')로 시작하는 이름을 선언하지 않음

    하나의 밑줄('_')로 시작하는 이름은 일반적으로 라이브러리 함수("_main" 및 "_exit")에서 사용합니다. 두 개의 밑줄("__")로 시작하거나 하나의 밑줄로 시작하고 그 다음에 소문자가 표시되는 이름은 컴파일러 내부 용도로 예약된 이름입니다.

    연속 밑줄 또한 정확한 밑줄 수를 파악하기 어려우므로 되도록 사용하지 않습니다.

    오케이 표시 손 모양 아이콘대소문자만 다른 유형 이름을 사용하지 않음

    대소문자만 다른 유형 이름은 그 차이를 기억하기가 어려워 쉽게 혼동됩니다.

    오케이 표시 손 모양 아이콘약어를 사용하지 않음

    약어는 응용프로그램 도메인에서 일반적으로 사용되거나(예: Fast Fourier Transform의 약어 FFT) 약어의 프로젝트 인식 목록에 정의되는 경우 사용될 수 있습니다. 그렇지 않은 경우 똑같지는 않지만 유사한 약어가 임의로 사용되어 혼란을 야기시키고 나중에 오류가 발생할 수 있습니다(예: track_identification의 약어로 trid, trck_id, tr_iden, tid, tr_ident 등을 사용할 수 있음).

    오케이 표시 손 모양 아이콘접미부를 사용하여 언어 구조를 표시하지 않음

    엔티티 유형을 분류(예: 유형의 경우 type, 예외의 경우 error)하기 위해 접미부를 사용하는 것은 일반적으로 코드를 이해하는 데 그다지 효과적이지 않습니다. arraystruct와 같은 접미부 또한 특정 구현을 나타냅니다. 즉, 구현 변경(구조체 또는 배열 표시 변경)이 발생하는 경우 클라이언트 코드에 나쁜 영향을 끼치거나 잘못된 결과가 발생하게 됩니다.

    그러나 접미부는 다음과 같은 여러 제한된 상황에서 유용합니다.

    오케이 표시 손 모양 아이콘명확하고 읽기 쉬우며 의미있는 이름 선택

    사용법 관점에서 이름을 선택하고 형용사와 명사를 함께 사용하여 로컬(컨텍스트 특정) 의미를 강화합니다. 또한 이름과 해당 유형이 일치해야 합니다.

    다음과 같은 구성의 이름을 선택합니다.

    object_name.function_name(...);
    object_name->function_name(...);      

    이러한 이름은 읽기에도 쉽고 올바른 의미를 나타냅니다.

    짧거나 축약된 이름을 사용하는 데 있어 입력 속도는 그다지 중요하지 않습니다. 단일 문자 및 짧은 ID는 일반적으로 잘못된 선택 또는 작성자의 게으름을 나타낼 뿐입니다. 일반적인 예외는 자연 대수의 기반으로 E를 사용하거나 Pi를 사용하는 경우입니다.

    컴파일러 또는 지원 도구에서 이름의 길이를 제한하는 경우도 있습니다. 따라서 긴 이름에서 끝 문자만 다른 경우 이러한 도구에서는 해당 구분 문자가 잘릴 수 있습니다.

    예제
    void set_color(color new_color)
    {
      ...
      the_color = new_color;
      ...
    }
    is better than:
    void set_foreground_color(color fg)
    
    and:
    oid set_foreground_color(color foreground);{
      ...
      the_foreground_color = foreground;
      ...
    }      

    첫 번째 예제의 이름 지정이 다른 두 가지보다 올바른 이름 지정입니다. new_color는 올바른 표현이며 해당 유형과도 일치하므로 함수 시맨틱을 강화합니다.

    두 번째 경우, 직관력이 있는 사용자라면 fg가 전경을 나타내는 것이라는 것을 알 수 있을 것입니다. 그러나 올바른 프로그래밍 스타일은 사용자의 직관 또는 추론에만 의존해서는 안됩니다.

    세 번째 경우, 선언 이외의 용도로 foreground 매개변수를 사용하는 경우 사용자는 foreground가 전경 색상을 의미하는 것으로 믿게 됩니다. 그러나 이 코드는 내재적으로 color로 변환될 수 있는 유형일 수 있습니다.

    참고

    이름을 명사와 형용사로 구성하고 이름이 해당 유형과 일치하도록 함으로써 자연어를 따르고 코드를 보다 읽기 쉽게 작성하고 시맨틱을 강화할 수 있습니다.

    오케이 표시 손 모양 아이콘이름에 올바른 철자 사용

    영어 단어로 작성하는 이름은 올바른 맞춤법에 유의하고 프로젝트 필수 양식(예: 영국식 또는 미국식 중 선택)을 준수해야 합니다. 이는 주석에도 해당됩니다.

    오케이 표시 손 모양 아이콘부울 오브젝트에 긍정적인 술부 절 사용

    부울 오브젝트, 함수 및 함수 인수의 경우, 긍정적 양식의 술부 절을 사용하십시오. 예를 들어, is_not_available이 아닌 found_it, is_available을 사용하십시오.

    근거

    부정적 술부를 사용하는 경우, 이중 부정은 더 이해하기 어렵습니다.

    이름 공간

    오케이 표시 손 모양 아이콘이름 공간을 사용하여 잠재적 글로벌 이름을 서브시스템 또는 라이브러리로 분할

    시스템을 서브시스템으로 분할하는 경우, 서브시스템 이름은 시스템의 글로벌 이름 공간을 분할 및 최소화하기 위한 이름 공간 이름으로서 사용하십시오. 시스템이 라이브러리인 경우, 전체 라이브러리에 가장 바깥쪽의 단일 이름 공간을 사용하십시오.

    각 서브시스템 또는 라이브러리 이름 공간에 의미있는 이름을 부여하십시오. 또한 약어 또는 동의어 별명을 부여하십시오. 일반적으로 충돌하지 않는 약어 또는 동의어 별명을 선택합니다. 예를 들어, ANSI C++ 표준 초안 라이브러리 [Plauger, 95]에서는 iso_standard_library에 대한 별명으로 std를 정의하십시오.

    컴파일러가 아직 이름 공간 구조를 지원하지 않는 경우, 이름 접두부를 사용하여 이름 공간을 시뮬레이션하십시오. 예를 들어, 시스템 관리 서브시스템 인터페이스의 공용 이름에는 syma(System Management의 약어)를 접두부로 사용할 수 있습니다.

    근거

    이름 공간을 사용하여 잠재적인 글로벌 이름을 묶으면 하위 프로젝트 팀 또는 벤더가 코드를 독립적으로 개발할 때 이름 충돌을 피할 수 있습니다. 결과적으로 이름 공간 이름만 글로벌 특성을 갖게 됩니다.

    클래스

    오케이 표시 손 모양 아이콘클래스 이름에 명사 또는 명사구 사용

    일반 명사 또는 명사구를 단일 양식으로 사용하여 해당 추상을 표현하는 이름을 클래스에 부여하십시오. 기본 클래스에는 보다 일반적인 이름을 사용하고 파생 클래스에는 보다 특수한 이름을 사용하십시오.

    typedef ... reference; // From the standard library
    typedef ... pointer;   // From the standard library
    typedef ... iterator;  // From the standard library
    class bank_account {...};
    class savings_account : public bank_account {...};
    class checking_account : public bank_account {...};      

    오브젝트 및 유형 모두에 대한 적합한 이름이 없거나 이름이 충돌하는 경우, 오브젝트에는 간단한 이름을 사용하고 유형 이름에는 mode, kind, code, 등과 같은 접미부를 추가하십시오.

    오브젝트 콜렉션을 나타내는 추상을 표현할 때는 복수 양식을 사용하십시오.

    typedef some_container<...> yellow_pages;      

    오브젝트 콜렉션 이상의 추가 시맨틱이 필요한 경우, 표준 라이브러리에서 다음을 동작 패턴으로 사용하고 접미부의 이름을 지정하십시오.

    함수

    오케이 표시 손 모양 아이콘프로시저 유형 함수 이름에 동사 사용

    리턴값이 없는 함수(무효 리턴 유형을 갖는 함수 선언)나 포인터 또는 참조 매개변수에 의해 값을 리턴하는 함수에는 동사 또는 조치 문구를 사용하십시오.

    유효 함수 리턴 유형에 의한 단일 값만 리턴하는 함수에는 명사를 사용하십시오.

    공통 오퍼레이션(동작 패턴)을 포함하는 클래스의 경우, 선택한 프로젝트 목록에서 파생된 오퍼레이션 이름을 사용하십시오 (예: 표준 라이브러리의 컨테이너 오퍼레이션의 경우 begin, end, insert, erase).

    특히 오브젝트 속성을 가져오거나 설정하기 위한 공용 오퍼레이션의 경우 "get" 및 "set" 이름 지정 특성을 사용(함수에 "get" 및 "set" 접두부 사용)하지 마십시오. 오퍼레이션 이름 지정은 서비스 레벨의 클래스 추상 및 준비에서 수행되어야 합니다. 오브젝트 속성 가져오기 및 설정은 공용 캡슐화를 약화시키는 하위 레벨 구현 세부사항입니다.

    부울(술부)을 리턴하는 함수에는 형용사(또는 과거 분사)를 사용하십시오. 술부의 경우, 일반적으로 명사 앞에 접두부 is 또는 has를 추가하면 이름을 긍정적 검증으로 읽을 수 있습니다. 이는 오브젝트, 유형 이름 또는 열거 리터럴에 이미 간단한 이름을 사용하는 경우에도 유용합니다. 이 경우 정확하고 일치하는 시제를 사용해야 합니다.

    예제
    void insert(...);
    void erase(...);
    
    Name first_name();
    bool has_first_name();
    bool is_found();
    bool is_available();      

    부정적인 이름을 사용하는 경우 이중 부정 표현식(예: !is_not_found)이 생성되어 코드를 이해하기 어렵습니다. 경우에 따라, "is_not_valid" 대신 "is_invalid"와 같은 반대어를 사용하여 해당 시맨틱을 변경하지 않고도 부정 술부를 긍정으로 만들 수도 있습니다.

    예제
    bool is_not_valid(...);
    void find_client(name with_the_name, bool& not_found);
    Should be re-defined as:
    bool is_valid(...);
    void find_client(name with_the_name, bool& found);      

    오케이 표시 손 모양 아이콘동일한 일반 의미를 나타낼 때 함수 오버로드 사용

    동일한 목적을 갖는 오퍼레이션의 경우, 동의어를 찾는 대신 오버로드를 사용합니다. 오버로드를 사용함으로써 시스템 내 오퍼레이션의 개념 및 변형 수가 최소화되어 전체 복잡도를 줄일 수 있습니다.

    연산자를 오버로드하는 경우, 연산자의 시맨틱을 유지해야 합니다. 연산자의 기존 의미를 유지할 수 없는 경우, 연산자를 오버로드하지 않고 다른 함수 이름을 선택합니다.

    오브젝트 및 함수 매개변수

    오케이 표시 손 모양 아이콘문법적 요소가 있는 접두 모음 이름으로 의미 강조

    고유성을 부각시키거나 이 엔티티가 조치의 중점 사항임을 나타내려면 오브젝트 또는 매개변수 이름의 접두부로 "the" 또는 "this"를 추가합니다. 2차, 임시, 보조 오브젝트를 나타내려면 다음과 같이 "a" 또는 "current"를 접두부로 추가합니다.

    예제
    void change_name( subscriber& the_subscriber,
    const subscriber::name new_name)
    {
      ...
      the_subscriber.name = new_name;
      ...
    }
    void update(subscriber_list& the_list,
                const subscriber::identification with_id,
                structure& on_structure,
                const value for_value);
    void change( object& the_object,
                const object using_object);      

    예외


    오케이 표시 손 모양 아이콘부정적 의미가 있는 예외 이름 선택

    예외는 오류 상황을 처리하는 용도로만 사용되어야 하므로 다음과 같이 부정적인 의미를 명확히 전달하는 명사 또는 명사구를 사용합니다.

    overflow, threshold_exceeded, bad_initial_value      

    오케이 표시 손 모양 아이콘예외 이름에 프로젝트 정의 형용사 사용

    특정 정보를 제공하지 않는 error 또는 exception을 체계적으로 사용하는 것보다 프로젝트 동의 목록에서 bad, incomplete, invalid, wrong, missing 또는 illegal과 같은 단어를 이름의 일부로서 사용합니다.

    기타

    오케이 표시 손 모양 아이콘부동 소수점 지수 및 16진수에 대문자 사용

    부동 소수점 리터럴의 문자 'E'와 16진수 'A' - 'F'는 항상 대문자여야 합니다.


    5장

    선언

    이 장에서는 다양한 C++ 선언 유형의 사용법과 양식에 대한 지침을 제공합니다.

    이름 공간

    C++ 언어의 이름 공간 기능 이전에는 한정된 방법으로만 이름 범위를 관리했습니다. 결과적으로 글로벌 이름 공간이 가득 차 충돌이 발생하고 일부 라이브러리를 동일한 프로그램에서 함께 사용할 수 없게 되었습니다. 그러나 새 이름 공간 언어 기능으로 글로벌 이름 공간 오염 문제점이 해결되었습니다.

    검지 아이콘글로벌 선언을 이름 공간만으로 제한

    즉, 이름 공간 이름만 글로벌 특성을 갖고 다른 모든 선언은 일부 이름 공간 범위에 속해야 합니다.

    이 규칙을 무시하면 결과적으로 이름이 충돌하게 됩니다.

    오케이 표시 손 모양 아이콘이름 공간을 사용하여 비클래스 기능 그룹화

    비클래스 기능의 논리 그룹(예: 클래스 카테고리) 또는 클래스보다 범위가 더 넓은 기능(예: 라이브러리 또는 서브시스템)의 경우, 이름 공간을 사용하여 논리적으로 선언을 통합합니다("이름 공간을 사용하여 잠재적 글로벌 이름을 서브시스템 또는 라이브러리로 분할" 참조).

    논리적 기능 그룹을 이름에 표현합니다.

    예제
    namespace transport_layer_interface { /* ... */ };
    namespace math_definitions { /* ... */ };      

    오케이 표시 손 모양 아이콘글로벌 및 이름 공간 범위 데이터 사용 최소화

    글로벌 및 이름 공간 범위 데이터를 사용하는 것은 캡슐화 원칙에 반하는 것입니다.

    클래스

    클래스는 C++의 기본 디자인 및 구현 단위입니다. 클래스는 도메인 및 디자인 추상을 캡처하기 위해서 또한 ADT(Abstract Data Type) 구현을 위한 캡슐화 메커니즘으로 사용해야 합니다.

    오케이 표시 손 모양 아이콘추상 데이터 유형을 구현하기 위해 struct 대신 class 사용

    추상 데이터 유형인 클래스를 구현하려면 struct 대신 class 클래스-키를 사용합니다.

    특히 C 코드와 상호작용할 때 POD(Plain-Old-Data) 구조를 C와 같이 정의하려면 struct 클래스-키를 사용합니다.

    classstruct는 동일하며 상호 교환하여 사용할 수 있지만 class에는 보다 효과적인 캡슐화를 위해 선호되는 기본 액세스 제어 강조(private)가 있습니다.

    근거

    classstruct를 구분하는 데 있어 일관된 사례를 채택함으로써 언어 규칙 이상의 시맨틱 차이가 도입됩니다. 즉, class는 추상 및 캡슐화를 캡처하기 위한 가장 중요한 구조체가 되고 struct는 혼합 프로그래밍 언어 프로그램에서 교환될 수 있는 순수 데이터 구조를 나타냅니다.

    오케이 표시 손 모양 아이콘액세스 가능성이 적어지는 순서대로 클래스 구성원 선언

    클래스 선언의 액세스 지정자는 public, protected, private 순서대로 표시되어야 합니다.

    근거

    구성원 선언의 public, protected, private 순서를 통해 클래스 사용자에게 가장 필요한 정보가 맨 처음에 표시될 수 있으며 결과적으로 클래스 사용자가 관련이 없거나 구현 세부사항을 탐색하지 않아도 됩니다.

    오케이 표시 손 모양 아이콘추상 데이터 유형에 대한 public 또는 protected 데이터 구성원을 선언하지 않음

    public 또는 protected 데이터 구성원을 사용함으로써 클래스 캡슐화가 줄어들고 시스템의 변경에 대한 탄력성에 영향을 줍니다. 즉, public 데이터 구성원은 클래스 구현을 해당 사용자에게 노출시키고 protected 데이터 구성원은 클래스 구현을 파생 클래스에 노출시킵니다. 클래스의 public 또는 protected 데이터 구성원에 대한 모든 변경사항은 사용자 및 파생 클래스에 영향을 줍니다.

    오케이 표시 손 모양 아이콘동반자를 사용하여 캡슐화 유지

    이 가이드라인은 처음 표시될 때 반직관적으로 표시됩니다. 동반자 관계에 따라 private 파트를 동반자에게 노출시키는 경우 캡슐화를 유지하는 방법은 무엇입니까? 클래스의 독립성이 매우 강하고 서로에 대한 내부 지식이 필요한 경우, 클래스 인터페이스를 통해 내부 세부사항을 내보내는 것보다 동반자 관계를 부여하는 것이 바람직합니다.

    내부 세부사항을 public 구성원으로서 내보냄으로써 클래스 클라이언트에 대한 바람직하지 않은 액세스 권한이 부여됩니다. protected 구성원을 내보냄으로써 잠재적인 후손에 대한 액세스 권한을 부여하게 되고 이를 통해 역시 바람직하지 않은 계층 구조 디자인이 생성됩니다. 동반자 관계는 서브클래스 제한조건을 강제 실행하지 않고도 선별적인 private 액세스 권한을 부여함으로써 액세스 권한이 필요한 거의 모든 대상으로부터 캡슐화를 유지합니다.

    동반자 관계를 사용한 캡슐화 유지의 올바른 예는 동반자 테스트 클래스에 동반자 관계를 부여하는 것입니다. 클래스 내부를 확인하는 동반자 테스트 클래스가 올바른 테스트 코드를 구현할 수 있으므로 나중에 제공된 코드에서 동반자 테스트 클래스가 삭제될 수 있습니다. 따라서, 캡슐화가 유실되지도 않고 인도물 코드에 추가로 코딩되지도 않습니다.

    오케이 표시 손 모양 아이콘클래스 선언에 함수 정의를 제공하지 않음

    클래스 선언에는 함수 선언만 포함되어야 하며 함수 정의(구현)는 포함될 수 없습니다.

    근거

    클래스 선언에 함수 정의가 제공되면 클래스 스펙이 구현 세부사항으로 오염됩니다. 이러한 경우 클래스 인터페이스를 인식하고 읽기가 어려워지고 컴파일 종속성이 증가합니다.

    클래스 선언의 함수 정의 또한 함수 인라인에 대한 제어를 방해합니다("No_Inline 조건부 컴파일 기호를 사용하여 인라인 컴파일 제거" 참조).

    검지 아이콘클래스의 기본 생성자에 항상 명시적으로 선언된 생성자 제공

    배열 또는 STL 컨테이너에서 클래스를 사용하려면 클래스가 공용 기본 생성자를 제공하거나 컴파일러가 생성할 수 있도록 허용해야 합니다.

    참고

    위의 규칙에 대한 예외는 클래스에 참조 유형의 비정적 데이터 구성원이 있는 경우입니다. 이러한 경우 일반적으로 의미있는 기본 구성자를 작성할 수 없습니다. 따라서 오브젝트 데이터 구성원에 대한 참조를 사용할 수 있는지 확신할 수 없습니다.

    검지 아이콘항상 포인터 유형 데이터 구성원을 사용하여 클래스에 대한 사본 생성자 및 대입 연산자 선언

    명확히 선언되지는 않았지만 필요한 경우, 컴파일러가 사본 생성자와 클래스에 대한 대입 연산자를 내재적으로 생성합니다. 컴파일러 정의 사본 생성자와 대입 연산자는 일반적으로 스몰토크(Smalltalk) 용어집에서 "얕은 복사(shallow-copy)"로 표기되는 조작을 구현합니다. 얕은 복사란 포인터에 대한 비트 단위 복사를 사용한 구성원 단위의 복사를 의미합니다. 컴파일러 생성 사본 생성자 및 기본 대입 연산자를 사용하면 메모리 누수가 발생합니다.

    예제
    // Adapted from [Meyers, 92].
    void f()
    {
    String hello("Hello");// Assume String is implemented 
                                      // with a pointer to a char 
                                      // array.
      { // Enter new scope (block)
      String world("World");
      world = hello;        // Assignment loses world's 
                            // original memory
      }	// Destruct world upon exit from 
         	// block;
         	// also indirectly hello
    String hello2 = hello; // Assign destructed hello to 
                           // hello2
    }      

    위의 코드에서 "World" 문자열을 보유하는 메모리는 대입 후 유실됩니다. 내부 블록을 종료할 때마다 world는 삭제되므로 hello가 참조하는 메모리 또한 유실됩니다. 해체된 hellohello2에 지정됩니다.

    예제
    // Adapted from [Meyers, 1992].
    void foo(String bar) {};
    void f()
    {
      String lost = "String that will be lost!";
      foo(lost);
    }      

    위의 코드에서, lost 인수를 사용하여 foo가 호출되면 컴파일러 정의 사본 생성자를 사용하여 lostfoo에 복사됩니다. lost는 포인터의 비트 단위 복사를 사용하여 "String that will be lost!"에 복사되므로 foo에서 종료할 때마다 lost의 사본은 "String that will be lost!"를 보유하는 메모리와 함께 소멸됩니다(소멸자가 올바르게 구현되어 메모리를 확보하는 것으로 가정).

    검지 아이콘생성자 매개변수가 기본값을 갖도록 재선언하지 않음

    예제
    // Example from [X3J16, 95; section 12.8]
    class X {
    public:
    X(const X&, int);	// int parameter is not 
                      				   // initialized
                      				   // No user-declared copy constructor, thus 
                      				   // compiler implicitly declares one.
    };
    // Deferred initialization of the int parameter mutates 
    // constructor into a copy constructor.
    //
    X::X(const X& x, int i = 0) { ... }      
    근거

    클래스 선언에서 "표준" 사본 생성자 서명을 확인하지 못하는 컴파일러는 내재적으로 사본 생성자를 선언합니다. 그러나 기본 매개변수의 초기화가 지연되는 경우 사본 생성자에 생성자가 복제되어 사본 생성자를 사용할 때 모호한 상황이 발생합니다. 즉, 사본 생성자를 사용하는 것은 모호성 문제로 인해 잘못된 방법입니다[X3J16, 95; section 12.8].

    검지 아이콘 항상 소멸자를 가상으로 선언

    클래스가 비파생 클래스로 명시적으로 디자인되지 않으면 해당 소멸자가 항상 가상으로 선언되어야 합니다.

    근거

    기본 클래스 소멸자가 가상으로 선언되지 않은 상태에서 포인터 또는 기본 클래스 유형에 대한 참조를 통해 파생 클래스 오브젝트를 삭제하면 정의되어 있지 않은 동작이 발생합니다.

    예제
    // Bad style used for brevity
    class B {
    public:
      B(size_t size) { tp = new T[size]; }
      ~B() { delete [] tp; tp = 0; }
      //...
    private:
      T* tp;
    };
    
    class D : public B {
    public:
      D(size_t size) : B(size) {}
      ~D() {}
      //... 
    };
    
    void f()
    {
    B* bp = new D(10);
    delete bp; // Undefined behavior due to
               		    // non-virtual base class
               	       // destructor
    }      

    오케이 표시 손 모양 아이콘너무 많은 변환 연산자 및 단일 매개변수 생성자를 선언하지 않음

    단일 매개변수 생성자는 또한 explicit 지정자를 사용하여 선언함으로써 내재적 변환에 사용되지 않도록 할 수 있습니다.

    검지 아이콘비가상 함수를 재정의하지 않음

    비가상 함수는 불변 동작을 구현하며 파생 클래스로 특화되지 않습니다. 이 가이드라인을 위반하는 경우 예상치 못한 동작이 발생할 수 있습니다. 예를 들어, 동일한 오브젝트가 다른 시점에 다른 동작을 나타낼 수 있습니다.

    비가상 함수는 통계적으로 바인딩됩니다. 따라서, 오브젝트에 대해 호출된 함수는 실제 오브젝트 유형이 아니라 아래 예제에서 각각 object-pointer-to-A 및 pointer-to-B를 참조하는 정적 변수 유형에 의해 제어됩니다.

    예제
    // Adapted from [Meyers, 92].
    class A {
    public:
      oid f(); // Non-virtual: statically bound
    };
    class B : public A {
    public:
      void f(); // Non-virtual: statically bound
    };
    void g()
    {
      B x;
      A* pA = &x; // Static type: pointer-to-A
      B* pB = &x; // Static type: pointer-to-B
      pA->f(); // Calls A::f
      pB->f(); // Calls B::f
    }      

    오케이 표시 손 모양 아이콘비가상 함수를 적절히 사용

    비가상 함수는 특화 및 다형성을 제한함으로써 서브클래스를 제한하므로, 오퍼레이션을 비가상으로 선언하기 전에 모든 서브클래스에 대해 불변인지 확인해야 합니다.

    오케이 표시 손 모양 아이콘생성자 내 지정 대신 생성자-초기화 설정자 사용

    구현/구축(Construction) 단계에서 오브젝트 상태의 초기화는 생성자 본문의 대입 연산자가 아닌 생성자 초기화 설정자(구성원 초기화 설정자 목록)가 수행해야 합니다.

    예제
    Do this:
    class X 
    {
    public:
      X();
    private
      Y the_y;
    };
    X::X() : the_y(some_y_expression) { } 
    //
    // "the_y" initialized by a constructor-initializer
    
    Rather than this:
    X::X() { the_y = some_y_expression; }
    //
    // "the_y" initialized by an assignment operator.      
    근거

    오브젝트 생성에는 생성자 본문을 실행하기 전 모든 기본 클래스 및 데이터 구성원에 대한 생성이 포함됩니다. 데이터 구성원을 초기화하려면 두 가지 오퍼레이션(생성 및 대입)이 필요합니다. 이 두 가지 오퍼레이션은 생성자-초기화 설정자를 사용하여 수행되는 단일 오퍼레이션(초기값을 사용한 생성)과 달리 생성자 본문에서 수행됩니다.

    대형 중첩 집계 클래스(다중 계층 클래스)의 경우복수 오퍼레이션(생성 및 구성원 대입)의 성능 오버헤드가 큽니다.

    검지 아이콘생성자 초기 설정자에게 구성원 함수를 호출하지 않음

    예제
    class A 
    {
    public:
      A(int an_int);
    };
    class B : public A
    {
    public:
      int f();
      B();
    };
    
    B::B() : A(f()) {} 
    // undefined: calls member function but A bas
    // not yet been initialized [X3J16, 95].      
    근거

    기본 클래스의 모든 구성원 초기화 설정자가 완료되기 전에 구성원 함수가 생성자 초기화 설정자에서 직간접적으로 호출되는 경우 오퍼레이션 결과를 정의할 수 없습니다[X3J16, 95].

    오케이 표시 손 모양 아이콘생성자 및 소멸자에서 구성원 함수를 호출하는 시기 파악

    생성자에서 구성원 함수를 호출할 때는 주의를 기울여야 합니다. 가상 함수가 호출되는 경우라도 실행된 함수는 생성자 또는 소멸자의 클래스 또는 해당 기본 클래스 중 하나에 정의된 함수입니다.

    오케이 표시 손 모양 아이콘필수 클래스 상수에 대한 static const 사용

    필수(정수) 클래스 상수를 정의하는 경우, #define 또는 글로벌 상수 대신 static const 데이터 구성원을 사용합니다. 컴파일러에서 static const를 지원하지 않는 경우 대신 enum의 상수를 사용합니다.

    예제
    Do this:
    class X {
      static const buffer_size = 100;
      char buffer[buffer_size];
    };
    static const buffer_size;
    
    Or this:
    class C {
      enum { buffer_size = 100 };
      char buffer[buffer_size];
    };
    
    But not this:
    #define BUFFER_SIZE 100
    class C {
      char buffer[BUFFER_SIZE];
    };      

    함수

    오케이 표시 손 모양 아이콘항상 명시적인 함수 리턴 유형 선언

    이 방법을 통해 컴파일러에서 명시적 리턴 유형 없이 선언된 함수의 리턴 유형이 누락된 경우 혼동을 막을 수 있습니다.

    오케이 표시 손 모양 아이콘함수 선언에 항상 정규 매개변수 이름 제공

    또한 함수 선언과 정의에 모두 동일한 이름을 사용합니다. 이를 통해 일관성을 극대화할 수 있습니다. 매개변수 이름을 지정하면 코드 문서화가 향상되고 읽기 쉬워집니다.

    오케이 표시 손 모양 아이콘단일 리턴 지점을 갖는 함수 사용

    함수 본문에 return 문이 너무 많은 경우 goto 문과 유사하여 코드를 읽거나 유지보수하기가 어렵습니다.

    return 문을 여러 번 사용할 수 있는 것은 모든 return 문을 동시에 볼 수 있고 코드 구조가 매우 일반적인 아주 짧은 함수에만 해당됩니다.

    type_t foo()
    {
      if (this_condition)
        return this_value;
      else
        return some_other_value;
    }      

    무효 리턴 유형이 있는 함수에는 return 문을 사용할 수 없습니다.

    오케이 표시 손 모양 아이콘글로벌 부작용이 있는 함수를 작성하지 않음

    글로벌 부작용(글로벌 및 이름 공간 데이터와 같은 내부 오브젝트 상태 이외의 잘 알려지지 않은 데이터 변경)을 발생시키는 함수 작성을 최소화해야 합니다("글로벌 및 이름 공간 범위 데이터의 사용 최소화" 참조). 그러나 가능한 경우 모든 부작용은 함수 스펙의 일부로 명확히 문서화해야 합니다.

    필수 오브젝트를 매개변수로서 전달함으로써 컨텍스트 종속성을 줄이고 보다 강력하면서도 쉽게 이해할 수 있는 컨텍스트가 생성됩니다.

    오케이 표시 손 모양 아이콘중요성 및 휘발성이 낮아지는 순서대로 함수 매개변수 선언

    매개변수가 선언되는 순서는 호출자 관점에서 중요합니다.

    이 순서를 통해 기본값을 활용하여 함수 호출 내 인수 수를 줄일 수 있습니다.

    오케이 표시 손 모양 아이콘매개변수 수가 가변적인 함수를 선언하지 않음

    매개변수 수가 가변적인 함수의 인수는 해당 유형을 확인할 수 없습니다.

    오케이 표시 손 모양 아이콘기본 매개변수로 함수를 재선언하지 않음

    나중에 함수를 재선언할 때 함수에 기본값을 추가해서는 안됩니다. 전방 선언을 제외한 모든 함수는 한 번만 선언해야 합니다. 이를 위반하는 경우 후속 선언을 알지 못하는 사용자에게 혼란을 가져다 줄 수 있습니다.

    오케이 표시 손 모양 아이콘함수 선언에서 const 사용 극대화

    함수에 상수 동작(상수 값 리턴, 상수 인수 허용 또는 부작용 없이 작동)이 있는지 여부를 확인하고 const 지정자를 사용하여 해당 동작을 검증합니다.

    예제
    const T f(...); // Function returning a constant
                         // object.
    T f(T* const arg);	// Function taking a constant
                         			          // pointer. 
    // The pointed-to object can be 
    // changed but not the pointer.
    T f(const T* arg);      // Function taking a pointer to, and
    T f(const T& arg);      // function taking a reference to a 
                            // constant object. The pointer can 
                            // change but not the pointed-to 
                            // object.
    T f(const T* const arg);  // Function taking a constant 
                              // pointer to a constant object. 
                              // Neither the pointer nor pointed-
                              // to object may change.
    T f(...) const;  // Function without side-effect: 
                     // does not change its object state; 
                     // so can be applied to constant 
                     // objects.      

    오케이 표시 손 모양 아이콘값으로 오브젝트를 전달하지 않음

    값으로 오브젝트를 전달하고 리턴하는 경우 과중한 생성자 및 소멸자 오버헤드가 발생할 수 있습니다. 생성자 및 소멸자 오버헤드는 오브젝트를 참조로 전달 및 리턴하여 피할 수 있습니다.

    Const 참조를 사용하면 참조로 전달된 인수를 수정할 수 없음을 지정할 수 있습니다. 일반 사용 예제는 다음과 같은 사본 생성자 및 대입 연산자입니다.

    C::C(const C& aC);
    C& C::operator=(const C& aC);      
    예제

    다음을 고려하십시오.

    the_class the_class::return_by_value(the_class a_copy)
    {
      return a_copy;
    }
    the_class an_object;
    return_by_value(an_object);      

    an_object를 인수로 사용하여 return_by_value가 호출되면 the_class 사본 생성자가 호출되어 an_objecta_copy에 복사합니다. the_class 사본 생성자는 다시 호출되어 a_copy를 함수 리턴 임시 오브젝트에 복사합니다. the_class 소멸자가 호출되면 a_copy가 함수에서 리턴될 때 삭제됩니다. the_class 소멸자는 나중에 다시 호출되어 return_by_value에서 리턴된 오브젝트를 삭제합니다. 아무 기능도 수행하지 않은 위의 함수 호출로 인해 두 개의 생성자와 두 개의 소멸자가 사용되었습니다.

    또한 the_class가 파생 클래스이고 다른 클래스의 포함 구성원 데이터인 경우에도 문제가 됩니다. 이 경우 기본 클래스와 포함된 클래스의 생성자 및 소멸자도 호출되어 함수 호출에 의해 발생한 생성자 및 소멸자 호출 수가 증가합니다.

    참고

    위의 가이드라인은 개발자가 항상 참조로 오브젝트를 전달 및 리턴해야 하는 것으로 이해될 수도 있지만 오브젝트가 필요한 경우 로컬 오브젝트 또는 참조에 대한 참조를 리턴하지 않도록 유의해야 합니다. 로컬 오브젝트에 대한 참조를 리턴하는 경우 함수 리턴 시 리턴된 참조가 삭제된 오브젝트와 바인딩된다는 점에서 문제의 발단이 됩니다.

    검지 아이콘 로컬 오브젝트에 대한 참조를 리턴하지 않음

    로컬 오브젝트는 함수 범위를 벗어날 때 삭제되므로 삭제된 오브젝트를 사용하는 것 또한 문제의 원인이 됩니다.

    검지 아이콘new로 초기화된 참조 해제 포인터를 리턴하지 않음

    이 가이드라인을 위반하면 메모리가 누수됩니다.

    예제
    class C {
    public:
      ...
    friend C& operator+( const C& left, 
                         const C& right);
    };
    C& operator+(const C& left, const C& right)
    {
      C* new_c = new C(left..., right...);
      return *new_c;
    }
    C a, b, c, d;
    C sum;
    sum = a + b + c + d;      

    연산자 +의 중간 결과는 합계를 계산할 때 저장되지 않으므로 중간 오브젝트가 삭제될 수 없으며 이로 인해 메모리 누수가 발생합니다.

    검지 아이콘구성원 데이터에 비const 참조 또는 포인터를 리턴하지 않음

    이 가이드라인을 위반하면 데이터 캡슐화도 위반하여 예상치 않은 결과가 발생할 수 있습니다.

    오케이 표시 손 모양 아이콘매크로 확장을 위해 #define보다 인라인 함수 사용

    그러나 인라인은 아주 짧은 함수에만 적절히 사용합니다. 긴 함수를 인라인하는 경우 코드가 길어질 수 있습니다.

    클라이언트 코드를 컴파일하려면 인라인 함수를 구현해야 하므로 인라인 함수는 또한 모듈 간의 컴파일 종속성을 증대시킵니다.

    [Meyers, 1992]는 다음과 같이 잘못된 매크로 사용법의 극단적인 예에 대한 자세한 설명을 제공합니다.

    예제

    다음은 잘못된 사용법입니다.

    #define MAX(a, b) ((a) > (b) ? (a) : (b))     

    다음을 올바른 사용법입니다.

    inline int max(int a, int b) { return a > b ? a : b; }      

    MAX 매크로에는 여러 가지 문제점이 있습니다. 즉, 안전한 유형도 아니고 해당 동작이 명확하지도 않습니다.

    int a = 1, b = 0;
    MAX(a++, b);     // a is incremented twice
    MAX(a++, b+10);  // a is incremented once
    MAX(a, "Hello"); // comparing ints and pointers      

    오케이 표시 손 모양 아이콘함수 오버로드보다 기본 매개변수 사용

    단일 알고리즘을 사용할 수 있는 경우 함수 오버로드보다는 기본 매개변수를 사용합니다. 알고리즘은 적은 수의 매개변수로 표현할 수 있습니다.

    기본 매개변수를 사용하면 오버로드 함수 수를 줄일 수 있어 유지보수가 간편하고 함수 호출에서 필요한 인수 수도 적어 코드를 쉽게 읽을 수 있습니다.

    오케이 표시 손 모양 아이콘함수 오버로드를 사용하여 공통 시맨틱 표현

    동일한 시맨틱 오퍼레이션에 인수 유형이 다른 여러 구현이 필요한 경우 함수 오버로드를 사용합니다.

    연산자 오버로드를 수행할 때는 규칙 내용을 잘 따라야 합니다. operator==operator!=와 같은 관련 연산자를 정의해서는 안됩니다.

    오케이 표시 손 모양 아이콘포인터 및 정수를 사용하여 함수를 오버로드하지 않음

    다음과 같이 정수 인수가 하나인 함수를 사용하여 포인터 인수가 하나인 함수를 오버로드하지 않습니다.

    void f(char* p);
    void f(int i);      

    다음 호출을 수행하면 예상치 않은 결과가 발생할 수 있습니다.

    f(NULL); f(0);  

    오버로드 해석에서는 f(char*)이 아닌 f(int)로 해석합니다.

    검지 아이콘 operator= *this에 대한 참조 리턴

    C++에서는 다음과 같이 대입 연산자를 연결할 수 있습니다.

    String x, y, z;
    x = y = z = "A string";      

    대입 연산자는 오른쪽 기준으로 결합되므로 "A string" 문자열은 z에, z는 y에, y는 x에 대입됩니다. operator=는 =의 오른쪽에서 각 표현식에 대해 오른쪽에서 왼쪽으로 효과적으로 한 번만 호출됩니다. 이는 또한 각 operator=의 결과는 오브젝트라는 의미이지만 오브젝트의 왼쪽 또는 오른쪽 리턴 여부는 선택할 수 있습니다.

    올바른 대입 연산자 서명의 양식은 다음과 같습니다.

    C& C::operator=(const C&);      

    여기서는 왼쪽 오브젝트만 가능하므로(rhs는 const 참조이고 lhs는 비const 참조) *this를 리턴해야 합니다. 자세한 설명은 [Meyers, 1992]를 참조하십시오.

    검지 아이콘자체 대입을 위한 operator= 확인

    이러한 확인을 수행하는 이유는 두 가지입니다. 먼저 파생 클래스 오브젝트의 대입에는 각 기본 클래스의 대입 연산자를 상속 계층 구조의 상위로 호출하는 작업이 포함되고 이러한 오퍼레이션을 건너뜀으로서 현저한 런타임 절감 효과를 얻을 수 있기 때문입니다. 두 번째로, 대입에는 "rvalue" 오브젝트를 복사하기 전에 "lvalue" 오브젝트 삭제가 포함됩니다. 자체 대입의 경우, rvalue 오브젝트가 대입되기 전에 삭제되므로 대입 결과를 정의할 수 없습니다.

    오케이 표시 손 모양 아이콘복잡도 최소화

    함수는 너무 길게(예: 60개 이상의 코드 행) 작성하지 않습니다.

    return 문의 수를 최소화합니다. 이상적인 수는 하나입니다.

    순환 복잡도는 10(단일 exit 문 함수에 대해 decision 문 수 + 1의 합계) 미만으로 유지합니다.

    확장 순환 복잡도는 15(단일 exit 문 함수에 대해 decision 문 수 + 논리 연산자 수 + 1의 합계) 미만으로 유지합니다.

    최대 참조 범위의 평균(로컬 오브젝트 선언 시점과 최초 사용 시점의 차이)을 최소화합니다.

    유형

    팁 아이콘프로젝트 전체의 글로벌 시스템 유형 정의

    대규모 프로젝트에서는 일반적으로 시스템 전체에서 자주 사용되는 유형 콜렉션이 있습니다. 이러한 경우 해당 유형을 하나 이상의 하위 레벨 글로벌 유틸리티 이름 공간에서 수집하는 것이 바람직합니다("기본 유형을 사용하지 않음"의 예제 참조).

    오케이 표시 손 모양 아이콘기본 유형을 사용하지 않음

    높은 수준의 이식성이 필요하거나 숫자 오브젝트가 사용하는 메모리 공간에 대한 제어가 필요하거나 특정 범위의 값이 필요한 경우 기본 유형을 사용해서는 안됩니다. 이러한 경우 해당 기본 유형을 사용하여 크기 제한조건으로 명시적 유형 이름을 선언하는 것이 바람직합니다.

    루프 카운터, 배열 색인 등을 통해 기본 유형이 다시 코드에 적용되지 않는지 확인합니다.

    예제
    namespace system_types {
      typedef unsigned char byte;
      typedef short int integer16; // 16-bit signed integer
      typedef int integer32; // 32-bit signed integer
      typedef unsigned short int natural16; // 16-bit unsigned integer
      typedef unsigned int natural32; // 32-bit unsigned integer
      ...
    }      
    근거

    기본 유형 표시는 구현과 관련이 있습니다.

    팁 아이콘동의어를 작성하여 로컬 의미를 강화하기 위해 typedef 사용

    typedef를 사용하여 기존 이름의 동의어를 작성하면 보다 의미있는 이름이 작성되어 쉽게 구분할 수 있습니다(이로 인한 런타임 불이익은 없습니다).

    typedef는 또한 규정된 이름에 대한 약칭을 제공하는 데 사용될 수 있습니다.

    예제
    // vector declaration from standard library
    //
    namespace std {
    template <class T, class Alloc = allocator>
    class vector {
    public:
      typedef typename 
      Alloc::types<T>reference reference;
      typedef typename 
      Alloc::types<T>const_reference const_reference;
      typedef typename 
      Alloc::types<T>pointer iterator;
      typedef typename 
      Alloc::types<T>const_pointer const_iterator;
      ...
    }
    }      

    typedef로 작성된 typedef 이름을 사용하는 경우, 원래 이름과 동의어를 동일한 코드에서 혼합 사용하지 않습니다.

    상수 및 오브젝트

    오케이 표시 손 모양 아이콘리터럴 값을 사용하지 않음

    이름이 지정된 상수를 사용합니다.

    오케이 표시 손 모양 아이콘상수 정의를 위해 프리프로세서 #define 지시문을 사용하지 않음

    대신 const 또는 enum을 사용합니다.

    다음은 잘못된 사용법입니다.

    #define LIGHT_SPEED 3E8
    
    다음을 올바른 사용법입니다.
    const int light_speed = 3E8;
    
    Or this for sizing arrays:
    enum { small_buffer_size = 100, 
    large_buffer_size = 1000 };      
    근거

    #defines로 생성된 이름은 컴파일 사전 처리 과정에서 대체되어 기호 테이블에 표시되지 않으므로 디버깅하기가 매우 어렵습니다.

    오케이 표시 손 모양 아이콘처음 사용 지점과 유사한 오브젝트 선언

    오케이 표시 손 모양 아이콘선언 시 항상 상수 오브젝트 초기화

    extern을 선언하지 않은 const 오브젝트에는 내부 링크가 있어 선언 시 이러한 상수 오브젝트를 초기화하면 초기화 설정자를 컴파일 시간에 사용할 수 있습니다.

    검지 아이콘상수 오브젝트의 "상수 특성"을 제거하지 않음

    상수 오브젝트는 읽기 전용 메모리에 존재합니다.

    오케이 표시 손 모양 아이콘정의 시 오브젝트 초기화

    오브젝트가 자체 초기화되지 않는 경우 오브젝트 정의에 초기값을 지정합니다. 의미있는 초기값을 지정할 수 없는 경우 "nil" 값을 지정하거나 오브젝트를 나중에 선언할 수 있습니다.

    대형 오브젝트의 경우, 많은 비용이 소요될 수 있으므로 일반적으로 오브젝트를 생성하지 않고 나중에 대입을 사용하여 초기화하는 것이 바람직합니다("생성자에서 대입 대신 생성자 초기화 설정자 사용" 참조).

    생성 시점에서 올바른 오브젝트 초기화를 수행할 수 없는 경우, "초기화되지 않음"을 나타내는 기존 "nil" 값을 사용하여 오브젝트를 초기화합니다. nil 값은 특정 알고리즘에 의해 제어되는 방식으로 거부될 수 있는 "사용할 수 없지만 알려진 값"을 선언하기 위한 초기화 용도로만 사용할 수 있습니다. 이 경우, 올바른 초기화 전에 오브젝트가 사용되면 초기화되지 않은 변수 오류가 표시됩니다.

    모든 유형(특히 각도와 같은 나머지 유형)에 항상 nil 값을 선언할 수 있는 것은 아닙니다. 이 경우 거의 불가능한 값을 선택합니다.


    6장

    표현식 및 명령문

    이 장에서는 다양한 유형의 C++ 표현식 및 명령문의 사용법과 양식에 대한 지침을 제공합니다.

    표현식

    팁 아이콘여유 괄호를 사용하여 보다 명확한 복합 표현식 작성

    팁 아이콘표현식을 너무 깊게 중첩시키지 않음

    표현식의 중첩 레벨은 연산자 우선순위 규칙이 무시된 경우 표현식을 왼쪽에서 오른쪽으로 평가하는 데 필요한 중첩 괄호 세트 수로 정의됩니다.

    중첩 레벨이 너무 많으면 표현식을 이해하기 어렵습니다.

    검지 아이콘특정 표현식 평가 순서를 가정하지 않음

    평가 순서를 연산자(쉼표 연산자, 삼진 표현식, 논리곱 및 논리합)로 지정하지 않는 경우, 특정 평가 순서를 가정해서는 안됩니다. 이 순서를 가정하면 예상치 않은 결과가 발생하고 이식할 수 없게 됩니다.

    예를 들어, 동일한 명령문에서 변수 사용을 변수의 인크리먼트 또는 디크리먼트로서 결합하지 않습니다.

    예제
    foo(i, i++);
    array[i] = i--;      

    팁 아이콘널(null) 포인터로 NULL 대신 O 사용

    널(null) 포인터에 0 또는 NULL을 사용하는 것은 논쟁의 여지가 있는 방법입니다.

    C 및 C++ 모두 0 값 상수 표현식을 널(null) 포인터로서 해석 가능한 것으로 정의합니다. 0은 읽기에 어렵고 리터럴은 사용하지 않는 것이 바람직하므로 프로그래머는 일반적으로 NULL 매크로를 널(null) 포인터로 사용해 왔습니다. 그러나 NULL에 대한 이식 불가능한 정의도 있습니다. 일부 ANSI C 컴파일러에서는 (void *)0을 사용해 왔지만 C++의 경우 올바른 선택이 아닌 것으로 밝혀졌습니다.

    char* cp = (void*)0; /* Legal C but not C++ */      

    따라서 C++의 경우, 단순한 0이 아닌 (T*)0 양식의 NULL 정의에는 캐스트가 필요합니다. 지금까지 널(null) 포인터에 대한 0 사용을 허용하는 가이드라인에서는 캐스팅 요구사항을 완화하여 코드 이식성을 향상시키고 있습니다. 그러나 많은 C++ 개발자는 0보다는 NULL을 사용하는 것이 더 편리하다고 생각하고 있으며 또한 대부분의 컴파일러(정확하게는 대부분의 헤더 파일)에서는 현재 NULL을 0으로 구현해야 하는 것으로 주장하고 있습니다.

    이 가이드라인 규칙에서는 0을 허용합니다. 0은 NULL 값에 관계 없이 작동하는 것으로 알려져 있지만 관련 논쟁으로 인해 이 사항은 팁 레벨로 조정되었으므로 판단에 따라 따르거나 무시할 수 있습니다.

    검지 아이콘하드코딩된 파일 경로 이름을 사용하지 않음

    이전 스타일 캐스팅보다는 새 캐스팅 연산자(dynamic_cast, static_cast, reinterpret_cast, const_cast)를 사용하십시오.

    새 캐스트 연산자가 없는 경우, 함께 캐스팅하지 않습니다. 특히 기본 클래스 오브젝트를 파생 클래스 오브젝트로 변환하는 다운캐스팅은 수행하지 않습니다.

    캐스팅 연산자를 사용하는 방법은 다음과 같습니다.

    유형 전환 로직을 구현하기 위해 typeid를 사용하지 않습니다. 캐스팅 연산자가 유형 검사 및 변환을 수행하도록 합니다. 자세한 설명은 [Stroustrup, 1994]를 참조하십시오.

    예제

    다음은 잘못된 사용법입니다.

    void foo (const base& b)
    {
      if (typeid(b) == typeid(derived1)) {
        do_derived1_stuff();
      else if (typeid(b) == typeid(derived2)) {
        do_derived2_stuff();
      else if () {
    
      }
    }      
    근거

    이전 스타일 캐스팅은 유형 시스템을 방해하여 컴파일러에서 감지하지 못한 버그를 감지할 수 없게 됩니다. 메모리 관리 시스템과 가상 함수 테이블이 손상될 수 있으며 오브젝트를 파생 클래스 오브젝트로서 액세스할 때 관련되지 않은 오브젝트 또한 손상될 수 있습니다. 이러한 경우 존재하지 않는 포인터 또는 필드가 참조될 수 있으므로 읽기 액세스로도 손상될 수 있습니다.

    새 스타일 캐스팅 연산자는 유형 변환을 보다 안전하고(대부분의 경우) 보다 명확하게 만듭니다.

    검지 아이콘부울 표현식에 새 부울 유형 사용

    이전 스타일 부울 매크로 또는 상수를 사용하지 않습니다. 표준 부울 값 true가 없으므로 대신 새 bool 유형을 사용합니다.

    검지 아이콘부울 값 true와 직접 비교하지 않음

    기존에 true에 대한 표준 값(1 또는 ! 0)이 없으므로 0이 아닌 표현식에 대한 비교는 수행할 수 없습니다.

    대신 부울 표현식을 사용합니다.

    예제

    다음은 잘못된 사용법입니다.

    if (someNonZeroExpression == true) 
    // May not evaluate to true
    
    Better to do this:
    if (someNonZeroExpression) 
    // Always evaluates as a true condition.      

    검지 아이콘동일한 배열에 없는 오브젝트와 포인터를 비교하지 않음

    이러한 연산의 결과는 일반적으로 의미가 없습니다.

    검지 아이콘삭제된 오브젝트 포인터에 항상 null 포인터 값 지정

    삭제된 오브젝트에 포인터를 널(null)로 설정하면 문제를 피할 수 있습니다. 널(null)이 아닌 포인터를 반복 삭제해도 아무런 문제가 없지만 널(null) 포인터를 반복 삭제하면 문제가 됩니다.

    나중에 새 코드가 추가될 수 있으므로 함수가 리턴되기 전이라도 삭제 후 널(null) 포인터 값을 지정합니다.

    명령문

    오케이 표시 손 모양 아이콘부울 표현식에 대한 분기를 수행할 때 if 문 사용

    오케이 표시 손 모양 아이콘비연속 값에 대한 분기를 수행할 때 switch 문 사용

    분기 조건이 불연속 값인 경우 일련의 "else if" 대신 switch 문을 사용합니다.

    검지 아이콘오류를 감지하기 위해 항상 switch 문에 default 분기 제공

    switch 문에는 항상 default 분기가 포함되어야 하므로 오류 감지를 위해서는 default 분기를 사용해야 합니다.

    이 정책을 통해, 새 전환 값이 대입되고 새 값을 처리하는 분기가 생략될 때 기존 기본 분기가 오류를 감지합니다.

    오케이 표시 손 모양 아이콘루프에서 사전 반복 테스트가 필요할 때 for 문 또는 while 문 사용

    반복 및 루프 종료가 루프 카운터를 기반으로 하는 경우 while 문보다 for 문을 사용합니다.

    오케이 표시 손 모양 아이콘루프에서 사후 반복 테스트가 필요할 때 do-while 문 사용

    오케이 표시 손 모양 아이콘루프에서 jump 문을 사용하지 않음

    루프 종료 조건을 사용하지 않고 break, return 또는 goto를 사용하여 루프에서 종료하지 않습니다. 또한 continue를 사용하여 미리 다음 반복으로 건너뛰지 않습니다. 이를 통해 일련의 제어 경로 수가 감소하여 코드를 보다 쉽게 이해할 수 있습니다.

    검지 아이콘 goto 문을 사용하지 않음

    이 가이드라인은 보편적인 가이드라인입니다.

    오케이 표시 손 모양 아이콘중첩된 범위에서 ID를 숨기지 않음

    이러한 경우 사용자의 혼란이 가중되어 잠재적인 유지보수 위험성이 발생할 수 있습니다.


    7장

    특수 주제

    이 장에서는 메모리 관리 및 오류 보고 주제에 대한 지침을 제공합니다.

    메모리 관리

    검지 아이콘 C 및 C++ 메모리 오퍼레이션을 혼합하지 않음

    오브젝트 공간을 할당하기 위해 C 라이브러리 malloc, callocrealloc 함수를 사용해서는 안됩니다. 이를 위해서는 C++ 연산자인 new를 사용해야 합니다.

    C 함수를 사용하여 메모리를 할당해야 하는 경우는 메모리를 제거하기 위해 C 라이브러리 함수로 전달되는 경우 뿐입니다.

    C 함수에 의해 할당된 메모리를 비우기 위해 delete를 사용해서는 안되며 new로 작성된 오브젝트에 free를 사용해서는 안됩니다.

    검지 아이콘new로 작성된 배열 오브젝트 삭제 시 항상 delete[] 사용

    비어 있는 대괄호("[]") 표기 없이 배열 오브젝트에 delete를 사용하는 경우 첫 번째 배열 요소만 삭제되므로 메모리 누수가 발생합니다.

    오류 처리 및 예외

    C++ 예외 메커니즘은 아직 많이 사용되지 않았으므로 이 문서에서 제공하는 가이드라인은 향후 상당 부분 개정될 수 있습니다.

    C++ 표준 초안은 두 가지 광범위한 오류 카테고리, 즉 로직 오류와 런타임 오류를 정의합니다. 로직 오류는 방지할 수 있는 프로그래밍 오류입니다. 런타임 오류는 프로그램 범위를 벗어난 이벤트로 인한 오류로 정의됩니다.

    예외 사용을 위한 일반 규칙은 정상 조건과 오버로드 또는 하드웨어 실패가 없는 경우 예외가 발생해서는 안된다는 것입니다.

    오케이 표시 손 모양 아이콘오류 발견을 위한 개발을 수행할 때 검증을 충분히 사용

    오류 감지 시 "프로그램이 중지되는" 오류 감지를 제공하기 위한 개발 과정에서는 함수 전제 조건 및 사후 조건 검증을 사용합니다.

    검증은 최종 오류 처리 코드가 구현될 때까지 간단하면서도 유용한 예비 오류 감지 메커니즘을 제공합니다. 검증에는 "NDEBUG" 프리프로세서 기호를 사용하여 컴파일할 수 있는 추가 이점이 있습니다("특정 값을 사용하여 NDEBUG 기호 정의" 참조).

    지금까지는 이러한 목적으로 검증 매크로가 사용되어 왔습니다. 그러나 참조[Stroustrup, 1994]는 다음과 같은 대체 템플리트를 제공합니다.

    예제
    template<class T, class Exception>
    inline void assert ( T a_boolean_expression, 
    Exception the_exception)
    {
      if (! NDEBUG)
      if (! a_boolean_expression) 
        throw the_exception;
    }      

    오케이 표시 손 모양 아이콘확실한 예외 조건에 대해서만 예외 사용

    자주 발생하는 예상 이벤트에는 예외를 사용하지 않습니다. 예외는 정상적인 코드 제어 플로우를 방해하여 이해 및 유지보수가 더 어렵기 때문입니다.

    예측 이벤트는 정상 코드 제어 플로우에서 처리해야 합니다. 필요에 따라 함수 리턴값 또는 "out" 매개변수 상태 코드를 사용합니다.

    또한 예외를 사용하여 제어 구조를 구현해서는 안됩니다. 이러한 경우 다른 양식의 "goto" 문이 필요합니다.

    오케이 표시 손 모양 아이콘표준 예외에서 프로젝트 예외 파생

    이를 통해 모든 예외가 최소 세트의 공통 오퍼레이션을 지원할 수 있으며 몇몇 상위 핸들러로 처리할 수 있습니다.

    로직 오류(도메인 오류, 올바르지 않은 인수 오류, 길이 오류 및 범위 초과 오류)는 응용프로그램 도메인 오류, 함수 호출로 전달된 올바르지 않은 인수, 해당 허용 크기를 초과하는 오브젝트 구조 및 허용 범위를 벗어난 인수 값을 표시하는 데 사용되어야 합니다.

    런타임 오류(범위 오류 및 오버플로우 오류)는 산술 및 구성 오류, 손상된 데이터 또는 런타임에서만 감지할 수 있는 자원 부족 오류를 표시하는 데 사용되어야 합니다.

    오케이 표시 손 모양 아이콘특정 추상에서 사용하여 예외 수 최소화

    대규모 시스템의 경우, 각 레벨에서 여러 예외를 처리하도록 하면 코드를 읽고 유지보수하기가 어렵습니다. 이러한 경우 예외 처리가 정상 처리를 방해할 수 있습니다.

    예외 수를 최소화하기 위한 방법은 다음과 같습니다.

    적은 수의 예외 카테고리를 사용하여 여러 추상이 예외를 공유합니다.

    표준 예외에서 파생된 특수 예외를 발행하고 보다 일반화된 예외를 처리합니다.

    오브젝트에 "예외" 상태를 추가하고 오브젝트의 유효성을 명시적으로 검사하는 기본요소를 제공합니다.

    오케이 표시 손 모양 아이콘처리된 모든 예외 선언

    예외를 통과할 뿐만 아니라 예외를 발생시키는 함수는 해당 예외 스펙에서 발행된 모든 예외를 선언해야 합니다. 이러한 함수는 클라이언트에 대한 경고 없이 자동으로 예외를 생성해서는 안됩니다.

    팁 아이콘최초 예외 발생 시 보고

    개발 단계에서는 가능한 빨리 적합한 로깅 메커니즘을 사용하여 예외를 보고합니다("전달 지점(throw-point)" 포함).

    오케이 표시 손 모양 아이콘가장 많이 파생되고 가장 기본이 되는 클래스 순서대로 예외 핸들러 정의

    도달할 수 없는 핸들러를 코딩하지 않으려면 가장 기본이 되는 파생 클래스 순서대로 예외 핸들러를 정의해야 합니다. 이러한 경우 또한 핸들러가 선언 순서대로 일치하여 가장 적합한 핸들러가 예외를 감지할 수 있습니다.

    예제

    다음은 잘못된 사용법입니다.

    class base { ... };
    class derived : public base { ... };
    ...
    try {
      ...
      throw derived(...);
      //
      // Throw a derived class exception
    }
    catch (base& a_base_failure)
      //
      // But base class handler "catches" because 
      // it matches first!
    {
      ...
    }
    catch (derived& a_derived_failure) 
    //
    // This handler is unreachable!
    { 
      ...
    }      

    오케이 표시 손 모양 아이콘 catch-all 예외 핸들러를 사용하지 않음

    에외가 다시 발행되지 않는 경우catch-all 예외 핸들러는 사용하지 않습니다.

    catch-all 핸들러는 로컬 관리에만 사용해야 하며, 이 레벨에서 예외를 처리할 수 없다는 사실을 나타내려면 예외를 재발행해야 합니다.

    try {
      ...
    }
    catch (...)
    { 
      if (io.is_open(local_file))
      {
        io.close(local_file);
      }
      throw;
    }      

    오케이 표시 손 모양 아이콘함수 상태 코드의 값이 올바른지 확인

    상태 코드를 함수 매개변수로 리턴하는 경우, 항상 함수 본문의 첫 번째 실행 가능 명령문으로서 매개변수에 값을 지정해야 합니다. 체계적으로 모든 상태를 기본적으로 성공 또는 실패로 작성하십시오. 예외 핸들러를 포함하여 가능한 모든 함수 종료 방법을 고려하십시오.

    오케이 표시 손 모양 아이콘로컬 안전 점검 수행, 클라이언트가 수행하지 않음

    올바른 입력이 제공되지 않아 함수에서 오류 출력이 생성되는 경우, 올바르지 않은 입력을 제어된 방식으로 감지하고 보고하는 코드를 함수에 설치합니다. 클라이언트에게 올바른 값을 전달하도록 지시하는 주석에 의존하지 마십시오. 해당 주석은 곧 무시되고, 올바르지 않은 매개변수가 감지되지 않는 경우 디버그하기 어려운 오류가 발생합니다.


    8장

    이식성

    이 장에서는 연역적으로 이식 불가능한 언어 기능에 대해 설명합니다.

    경로 이름

    검지 아이콘하드코딩된 파일 경로 이름을 사용하지 않음

    경로 이름은 모든 운영 체제에서 표준 방식으로 표시되지 않습니다. 하드 코딩된 이름을 사용하는 경우 플랫폼 종속성 문제가 발생합니다.

    예제
    #include "somePath/filename.hh" // Unix
    #include "somePath\filename.hh" // MSDOS      

    데이터 표시

    유형의 표시와 배열은 시스템 아키텍처에 대한 의존도가 매우 높습니다. 따라서 표시 및 배열에 대한 가정으로 인해 예상치 않은 결과가 발생하거나 이식성이 저하될 수 있습니다.

    검지 아이콘 유형 표시를 가정하지 않음

    특히, 이식성이 낮은 long 또는 기타 숫자 유형인 int에 포인터를 저장하지 않습니다.

    검지 아이콘유형 배열을 가정하지 않음

    검지 아이콘특정 언더플로우 또는 오버플로우 동작에 의존하지 않음

    오케이 표시 손 모양 아이콘가능한 경우 "확장 가능" 상수 사용

    확장 가능 상수를 통해 단어 크기 차이에 따른 문제점을 막을 수 있습니다.

    예제
    const int all_ones = ~0;
    const int last_3_bits = ~0x7;     

    유형 변환

    검지 아이콘"짧은" 유형에서 "긴" 유형으로 변환하지 않음

    시스템 아키텍처는 특정 유형의 배열을 나타낼 수 있습니다. 보다 자유로운 배열 요구사항의 유형에서 보다 엄격한 배열 요구사항의 유형으로 변환하는 경우 프로그램에 문제가 발생할 수 있습니다.


    9장

    재사용

    이 장에서는 C++ 코드 재사용에 대한 지침을 제공합니다.

    오케이 표시 손 모양 아이콘가능한 경우 표준 라이브러리 컴포넌트 사용

    표준 라이브러리를 사용할 수 없는 경우, 표준 라이브러리 인터페이스를 기반으로 한 클래스를 작성하며 이를 통해 향후 이주 작업이 보다 용이해집니다.

    오케이 표시 손 모양 아이콘템플리트를 사용하여 데이터 독립 동작 재사용

    해당 동작이 특정 데이터 유형에 의존하지 않는 경우 템플리트를 사용하여 동작을 재사용하십시오.

    오케이 표시 손 모양 아이콘public 상속을 사용하여 클래스 인터페이스 재사용(하위 입력)

    public 상속을 사용하여 "isa" 관계를 표현하고 기본 클래스 인터페이스 및 선택적으로 해당 구현을 재사용하십시오.

    오케이 표시 손 모양 아이콘private 상속이 아닌 포함을 사용하여 클래스 구현 재사용

    구현을 재사용하거나 "부분/전체" 관계를 모델링하는 경우 private 상속을 사용하지 않습니다. 재정의 없이 구현을 재사용하려면 private 상속보다 포함이 가장 효과적입니다.

    기본 클래스 오퍼레이션에 대한 재정의가 필요한 경우 private 상속을 사용하십시오.

    오케이 표시 손 모양 아이콘복수 상속을 적절히 사용

    복수 상속의 경우 복잡도가 증가하므로 주의해서 사용해야 합니다. [Meyers, 1992]는 잠재적인 이름 모호성과 반복 상속으로 인한 복잡도에 대해 자세한 설명을 제공합니다. 복잡도가 증가하는 원인은 다음과 같습니다.

    여러 클래스에서 동일한 이름을 사용하는 경우 이름에 대한 규정되지 않은 참조는 모호합니다. 이러한 모호성은 구성원 이름을 해당 클래스 이름으로 규정하여 해결할 수 있습니다. 그러나 이 방법을 사용하는 경우 다형성이 저하되고 가상 함수가 정적으로 바인딩된 함수로 변환되는 단점이 있습니다.

    동일한 기본 클래스의 복수 데이터 구성원 세트에 대한 반복 상속(상속 계층 구조 내 다른 경로를 통한 파생 클래스에 의한 기본 클래스의 복수 상속)은 복수 데이터 구성원 세트 중 사용할 세트를 선택해야 하는 문제점을 발생시킵니다.

    복수 상속되는 데이터 구성원은 가상 상속(가상 기본 클래스의 상속)을 사용하여 막을 수 있습니다. 이러한 가상 상속을 항상 사용할 수 없는 이유는 가상 상속을 수행하는 경우 기본 오브젝트 표시를 변경하여 액세스 효율성이 저하되는 부정적인 영향을 주기 때문입니다.

    모든 상속을 가상 상속으로 지정하는 정책을 규정하는 동시에 모든 주위 공간 및 시간 부담을 지우는 것은 지나치게 강제적인 작업입니다.

    따라서 복수 상속의 경우 클래스 디자이너가 향후 클래스 사용에 대한 통찰력을 갖고 있어야 가상 또는 비가상 상속 사용에 대한 올바른 결정을 내릴 수 있습니다.


    10장

    컴파일 문제

    이 장에서는 컴파일 문제에 대한 지침을 제공합니다.

    오케이 표시 손 모양 아이콘컴파일 종속성 최소화

    모듈 스펙에 모듈 구현에서만 필요한 다른 헤더 파일을 포함하지 마십시오.

    다른 클래스에 대한 가시성을 확보하기 위해 스펙에 헤더 파일을 포함하지 마십시오. 포인터 또는 참조 가시성만 필요한 경우 전방 선언을 대신 사용하십시오.

    예제
    // Module A specification, contained in file "A.hh"
    #include "B.hh" // Don't include when only required by
                    // the implementation.
    #include "C.hh" // Don't include when only required by
                    // reference; use a forward declaration instead.
    class C;
    class A
    {
      C* a_c_by_reference; // Has-a by reference.
    };
    // End of "A.hh"      
    참고

    컴파일 종속성 최소화는 다양하게 이름 지정되는 특정 디자인 용어 또는 패턴의 근거입니다(예: 핸들(handle) 또는 엔벨로프(envelope) [Meyers, 1992], 또는 Bridge [Gamma] 클래스). 클래스 추상에 대한 책임을 두 개의 연관 클래스(즉, 클래스 인터페이스를 제공하는 클래스와 구현을 제공하는 클래스)에 분할하는 경우, 구현(구현 클래스) 변경 시 더 이상 클라이언트가 재컴파일되지 않으므로 클래스와 해당 클라이언트 간의 종속성이 최소화됩니다.

    예제
    // Module A specification, contained in file "A.hh"
    class A_implementation;
    class A
    {
      A_implementation* the_implementation;
    };
    // End of "A.hh"      

    이러한 접근 방식을 통해 또한 인터페이스 클래스와 구현 클래스가 두 개의 별도 클래스 계층 구조로 특화될 수 있습니다.

    검지 아이콘 NDEBUG 기호를 특정 값으로 정의

    NDEBUG 기호는 지금까지 검증 매크로를 사용하여 구현된 검증 코드를 컴파일하는 데 사용되었습니다. 종래의 사용법 패러다임은 검증을 제거해야 하는 경우 기호를 정의하는 것이었습니다. 그러나 개발자는 일반적으로 이런 검증이 있다는 사실을 알지 못하여 기호를 정의하지 않았습니다.

    이 문서에서는 검증의 템플리트 버전을 사용할 수 있도록 허용하며, 이러한 경우 NDEBUG 기호에 명시적인 값이 제공되어야 합니다. 검증 코드가 필요한 경우 해당 값은 0이고 제거해야 하는 경우 0이 아닌 값이 지정됩니다. NDEBUG 기호에 특정 값을 제공하지 않고 나중에 컴파일된 검증 코드는 컴파일 오류를 생성하므로 개발자는 검증 코드의 존재 여부에 주의를 기울여야 합니다.


    가이드라인 요약

    다음은 이 문서에서 제공하는 모든 가이드라인에 대한 요약 정보입니다.

    검지 아이콘요구사항 또는 제한조건

    상식 사용
    항상 #include를 사용하여 모듈 스펙에 대한 액세스 확보
    하나 이상의 밑줄('_')로 시작하는 이름을 선언하지 않음
    글로벌 선언을 이름 공간만으로 제한
    클래스의 기본 생성자에 항상 명시적으로 선언된 생성자 제공
    항상 포인터 유형 데이터 구성원을 사용하여 클래스에 대한 사본 생성자 및 대입 연산자 선언
    생성자 매개변수가 기본값을 갖도록 재선언하지 않음
    항상 소멸자를 가상으로 선언
    비가상 함수를 재정의하지 않음
    생성자 초기 설정자에게 구성원 함수를 호출하지 않음
    로컬 오브젝트에 대한 참조를 리턴하지 않음
    new로 초기화된 참조 해제 포인터를 리턴하지 않음
    구성원 데이터에 비const 참조 또는 포인터를 리턴하지 않음
    operator=*this에 대한 참조 리턴
    자체 대입을 위한 operator= 확인
    상수 오브젝트의 "상수 특성"을 제거하지 않음
    특정 표현식 평가 순서를 가정하지 않음
    이전 스타일 캐스팅을 사용하지 않음
    부울 표현식에 새 bool 유형 사용
    부울 값 true와 직접 비교하지 않음
    동일한 배열에 없는 오브젝트와 포인터를 비교하지 않음
    삭제된 오브젝트 포인터에 항상 null 포인터 값 지정
    오류를 감지하기 위해 항상 switch 문에 기본 분기 제공
    goto 문을 사용하지 않음
    C 및 C++ 메모리 오퍼레이션을 혼합하지 않음
    new로 작성된 배열 오브젝트 삭제 시 항상 delete[] 사용
    하드코딩된 파일 경로 이름을 사용하지 않음
    유형 표시를 가정하지 않음
    유형 배열을 가정하지 않음
    특정 언더플로우 또는 오버플로우 동작에 의존하지 않음
    "짧은" 유형에서 "긴" 유형으로 변환하지 않음
    NDEBUG 기호를 특정 값으로 정의

    오케이 표시 손 모양 아이콘권장사항

    모듈 스펙과 구현을 별도 파일에 배치
    헤더와 구현 파일을 구분하는 단일 파일 이름 확장자 세트 선택
    모듈 스펙당 두 개 이상의 클래스 정의
    implementation-private 선언을 모듈 스펙에 두지 않음
    별도 파일에 모듈 인라인 함수 정의 배치
    프로그램 크기가 문제인 경우 대형 모듈을 여러 변환 단위로 분할
    플랫폼 종속성 분리
    반복 파일 포함으로부터 보호
    "No_Inline" 조건부 컴파일 기호를 사용하여 인라인 컴파일 제거
    중첩된 명령문에 짧고 일관된 들여쓰기 스타일 사용
    함수 이름 또는 범위 이름에서 함수 매개변수 들여쓰기
    표준 인쇄 용지 크기에 맞는 최대 행 길이 사용
    일관된 행 접기 사용
    C 스타일 주석 대신 C++ 스타일 주석 사용
    소스 코드에 대한 주석 근접성 극대화
    행 끝 주석을 사용하지 않음
    주석 헤더를 사용하지 않음
    비어 있는 주석 행을 사용하여 주석 단락 구분
    중복을 피함
    주석 대신 자체 문서화 코드 작성
    클래스 및 함수 문서화
    이름 지정 규칙을 선택하여 일관되게 적용
    대소문자만 다른 유형 이름을 사용하지 않음
    약어를 사용하지 않음
    접미부를 사용하여 언어 구조를 표시하지 않음
    명확하고 읽기 쉬우며 의미있는 이름 선택
    이름에 올바른 철자 사용
    부울 오브젝트에 긍정적인 술부 절 사용
    이름 공간을 사용하여 잠재적 글로벌 이름을 서브시스템 또는 라이브러리로 분할
    클래스 이름에 명사 또는 명사구 사용
    프로시저 유형 함수 이름에 동사 사용
    동일한 일반 의미를 나타낼 때 함수 오버로드 사용
    문법적 요소가 있는 접두 모음 이름으로 의미 강조
    부정적 의미가 있는 예외 이름 선택
    예외 이름에 프로젝트 정의 형용사 사용
    부동 소수점 지수 및 16진수에 대문자 사용
    이름 공간을 사용하여 비클래스 기능 그룹화
    글로벌 및 이름 공간 범위 데이터 사용 최소화
    추상 데이터 유형을 구현하기 위해 struct 대신 class 사용
    액세스 가능성이 적어지는 순서대로 클래스 구성원 선언
    추상 데이터 유형에 대한 public 또는 protected 데이터 구성원을 선언하지 않음
    동반자를 사용하여 캡슐화 유지
    클래스 선언에 함수 정의를 제공하지 않음
    너무 많은 변환 연산자 및 단일 매개변수 생성자를 선언하지 않음
    비가상 함수를 적절히 사용
    생성자 내 지정 대신 생성자-초기화 설정자 사용
    생성자 및 소멸자에서 구성원 함수를 호출하는 시기 파악
    필수 클래스 상수에 대한 static const 사용
    항상 명시적인 함수 리턴 유형 선언
    함수 선언에 항상 정규 매개변수 이름 제공
    단일 리턴 지점을 갖는 함수 사용
    글로벌 부작용이 있는 함수를 작성하지 않음
    중요성 및 휘발성이 낮아지는 순서대로 함수 매개변수 선언
    매개변수 수가 가변적인 함수를 선언하지 않음
    기본 매개변수로 함수를 재선언하지 않음
    함수 선언에서 상수 사용 극대화
    값으로 오브젝트를 전달하지 않음
    매크로 확장을 위해 #define보다 인라인 함수 사용
    함수 오버로드보다 기본 매개변수 사용
    함수 오버로드를 사용하여 공통 시맨틱 표현
    포인터 및 정수를 사용하여 함수를 오버로드하지 않음
    복잡도 최소화
    기본 유형을 사용하지 않음
    리터럴 값을 사용하지 않음
    상수 정의를 위해 프리프로세서 #define 지시문을 사용하지 않음
    처음 사용 지점과 유사한 오브젝트 선언
    선언 시 항상 상수 오브젝트 초기화
    정의 시 오브젝트 초기화
    부울 표현식에 대한 분기를 수행할 때 if 문 사용
    비연속 값에 대한 분기를 수행할 때 switch 문 사용
    루프에서 사전 반복 테스트가 필요할 때 for 문 또는 while 문 사용
    루프에서 사후 반복 테스트가 필요할 때 do-while 문 사용
    루프에서 jump 문을 사용하지 않음
    중첩된 범위에서 ID를 숨기지 않음
    오류 발견을 위한 개발을 수행할 때 검증을 충분히 사용
    확실한 예외 조건에 대해서만 예외 사용
    표준 예외에서 프로젝트 예외 파생
    특정 추상에서 사용하여 예외 수 최소화
    처리된 모든 예외 선언
    가장 많이 파생되고 가장 기본이 되는 클래스 순서대로 예외 핸들러 정의
    catch-all 예외 핸들러를 사용하지 않음
    함수 상태 코드의 값이 올바른지 확인
    로컬 안전 점검 수행, 클라이언트가 수행하지 않음
    가능한 경우 "확장 가능" 상수 사용
    가능한 경우 표준 라이브러리 컴포넌트 사용

    팁 아이콘

    프로젝트 전체의 글로벌 시스템 유형 정의
    동의어를 작성하여 로컬 의미를 강화하기 위해 typedef 사용
    여유 괄호를 사용하여 보다 명확한 복합 표현식 작성
    표현식을 너무 깊게 중첩시키지 않음
    널(null) 포인터로 NULL 대신 O 사용
    최초 예외 발생 시 보고


    참고 문헌

    [Cargill, 92] Cargill, Tom. 1992. C++ Programming Styles Addison-Wesley.

    [Coplien, 92] Coplien, James O. 1992. Advanced C++ Programming Styles and Idioms, Addison-Wesley.

    [Ellemtel, 93] Ellemtel Telecommunications Systems Laboratories. June 1993. Programming in C++ Rules and Recommendations.

    [Ellis, 90] Ellis, Margaret A. and Stroustrup, Bjarne.1990. The Annotated C++ Reference Manual, Addison-Wesley.

    [Kruchten, 94] Kruchten, P. May 1994. Ada Programming Guidelines for the Canadian Automated Air Traffic System.

    [Lippman, 96] Lippman, Stanley, B. 1996. Inside the C++ Object Model, Addison-Wesley.

    [Meyers, 92] Meyers, Scott. 1992. Effective C++, Addison-Wesley.

    [Meyers, 96] Meyers, Scott. 1996. More Effective C++, Addison-Wesley.

    [Plauger, 95] Plauger, P.J. 1995. The Draft Standard C++ Library, Prentice Hall, Inc.

    [Plum, 91] Plum, Thomas and Saks, Dan. 1991. C++ Programming Guidelines, Plum Hall Inc.

    [Stroustrup, 94] Stroustrup, Bjarne. 1994. The Design and Evolution of C++, Addison-Wesley.

    [X3J16, 95] X3J16/95-0087 | WG21/N0687. April 1995. Working Paper for Draft Proposed International Standard for Information Systems-Programming Language C++.