HICPP(High Integrity C++) 코딩 표준

High Integrity C++ (HIC++ 또는 HICPP)

  • Programming Research Limited(PRQA)에 의해 개발된 C++ 프로그래밍 언어를 위한 소프트웨어 코딩 표준
  • 2003년 10월 3일, 202개 규칙으로 구성된 HIC++ 코딩 표준 최초 버전이 출시됨
  • 2011년 C++ 언어의 주요 변경이 비준됨(ISO C++ 2011)에 따라 HIC++ 코딩 표준도 주요 업데이트가 이루어졌으며, 2013년 10월 155개 규칙으로 구성된 버전 4.0이 출시됨

HIC++ 준수를 체킹하는 자동 도구

  • Programming Research Limited가 개발한 QA·C++
  • Liverpool Data Research Associates(LDRA)가 개발한 LDRA Testbed/TBvision과 LDRArules

HIC++ 버전 4.0

아래에 주요 규칙의 헤딩만 한글로 나열함. 각각의 상세한 설명과 코드 예는 출처에 나온 웹 사이트 참조

1 일반(General)

1.1 구현 준수(Implementation compliance)1.1.1 코드가 2011 ISO C++ 언어 표준을 준수해야 한다.
1.2 중복(Redundancy)1.2.1 모든 문장(statements)이 도달가능(reachable) 해야 한다.
1.2.2 중복되는 표현(expression) 또는 하위표현(sub-expression)이 없어야 한다.
1.3 소멸된 기능(Deprecated features)1.3.1 bool 타입의 변수 상에 증가 연산자(++)를 사용 안 한다.
1.3.2 register 키워드를 사용 안 한다.
1.3.3 C 표준 라이브러리 .h 헤더를 사용 안 한다.
1.3.4 소멸된 STL 라이브러리 기능을 사용 안 한다.
1.3.5 throw 예외 명세를 사용 안 한다.

2 어휘 관례(Lexical conventions)

2.1 문자 집합(Character sets)2.1.1 소스 파일에 탭 문자(tab characters)를 사용 안 한다.
2.2 3중음자(Trigraph sequences)2.2.1 이중 글자(digraphs) 또는 3중 글자(trigraphs)를 사용 안 한다.
2.3 코멘트(Comments)2.3.1 C 코멘트 구분자 /* … */를 사용 안 한다.
2.3.2 구 코드를 제거하는 대신 코멘트 처리하여 보관하지 않는다.
2.4 식별자(Identifiers)2.4.1 각 식별자가 다른 가시적인 식별자와 확연히 다르다(구별된다).
2.5 리터럴(Literals)2.5.1 스트링을 다른 인코딩 접두사(encoding prefixes)와 연쇄하지(concatenate) 않는다.
2.5.2 0을 제외하고는 8진 상수(octal constants)를 사용하지 않는다.
2.5.3 널 포인터 상수를 위해 nullptr를 사용한다.

3 기본 개념(Basic concepts)

3.1 범위(Scope)3.1.1 숨겨진 선언(declarations)을 사용하지 않는다. 동일한 식별자(identifier)를 여러 다른 선언을 위해 재사용하는 것은 혼돈스럽고 유지보수가 어려움
3.2 프로그램과 링키지(Program and linkage)3.2.1 블록 범위에서 함수(functions)를 선언하지 않는다. 모든 선언에서 동일한 타입이 사용되는 것을 보장하기 위해 함수가 항상 네임스페이스 범위에서 선언되어야 함
3.3 기억 기간(Storage duration)3.3.1 정적 기억 기간을 가진 변수(글로벌 변수)를 사용하지 않는다.
3.4 오브젝트 수명(Object lifetime)3.4.1 리퍼런스 또는 포인터를 함수 내에 정의된 자동 변수로 반환하지 않는다.
3.4.2 변수 주소를 더 긴 수명을 가진 포인터로 할당하지 않는다.
3.4.3 리소스를 위해 RAII(Resource Acquisition Is Initialization)를 사용한다. 매니저 클래스를 사용함으로써 리소스의 수명이 정확하게 통제될 수 있음
3.5 타입(Types)3.5.1 값 또는 오브젝트의 내적 표현(internal representation)에 대한 어떤 가정도 하지 않는다.

4 표준 전환(Standard conversions)

4.1 어레이에서 포인터로 전환(Array-to-pointer conversion)4.1.1 함수 인자가 어레이에서 포인터로의 전환을 겪지 않도록 보장한다.
4.2 인테그랄 전환(Integral conversions)4.2.1 U 접미사가 무부호 인테그랄 표현식을 요구하는 문맥(context)에 사용되는 리터럴에 적용되는 것을 보장한다.
4.2.2 integral 표현식에서 데이터 손실이 명백히 발생하지 않음을 보장한다.
4.3 부동 소수점 전환(Floating point conversions)4.3.1 더 넓은 부동 소수점 타입의 표현식을 그보다 좁은 부동 소수점 타입으로 전환하지 않는다.
4.4 부동 소수점에서 인테그랄로 전환(Floating-integral conversions)4.4.1 부동 소수점 값을 인테그랄 타입으로 전환하지 않는다(표준 라이브러리 함수 사용을 통해서는 예외).

5 표현식(Expressions)

5.1 일차식(Primary expressions)5.1.1 코드에서 리터럴 값 대신에 상징적 이름을 사용한다.
5.1.2 한 표현식 내에서 평가의 순서에 의존하지 않는다.
5.1.3 표현의 의도를 명확히 하기 위해 표현식에서 괄호를 사용한다.
5.1.4 lambda에서 암묵적으로 변수를 캡쳐하지 않는다.
5.1.5 모든 lambda 표현식에서 (비어있는) 패러미터 목록을 포함한다. 다른 C++ 구성물(constructs)과의 시각적 애매모호성을 피하기 위함
5.1.6 &&, ||, sizeof, typeid, 또는 condition_variable::wait로 전달되는 함수의 우측 피연산자로 부작용(side effects)이 코드되지 않도록 한다.
5.2 후치식(Postfix expressions)5.2.1 포인터 접근 또는 어레이 접근이 명백히 유효한 오브젝트의 경계 내에 있도록 보장한다.
5.2.2 함수가 직접적으로든 간접적으로든 스스로를 호출하지 않음을 보장한다.
5.3 단항식(Unary expressions)5.3.1 무부호 타입(unsigned type)의 피연산자로 단항 마이너스 연산자(-)를 적용하지 않는다. 대신 비트단위 보수 연산자(~) 사용을 선호한다.
5.3.2 메모리를 new를 사용해 할당하고, 그것을 delete를 사용해 할당 해제 한다.
5.3.3 delete의 형태가 메모리를 할당하는데 사용된 new의 형태와 매치됨을 보장한다.
5.4 노골적인 타입 전환(Explicit type conversion)5.4.1 오로지 static_cast (void* 제외), dynamic_cast, 또는 노골적인 생성자(constructor) 호출의 캐스팅 형태만 사용한다.
5.4.2 표현식을 열거형(enumeration) 타입으로 캐스트 하지 않는다.
5.4.3 베이스 클래스로부터 파생 클래스로 전환하지 않는다. 이런 캐스트가 불가피하다면 static_cast 보다는 dynamic_cast가 사용되어야 함(컴파일러가 런타임 시 캐스트의 유효성을 체크함)
5.5 곱셈 연산자(Multiplicative operators)5.5.1 division 연산자 또는 remainder 연산자의 우측 피연산자가 명백히 0이 아님을 보장한다.
5.6 시프트 연산자(Shift operators)5.6.1 bitwise 연산자를 부호 피연산자(signed operands)와 함께 사용하지 않는다.
5.7 동등 연산자(Equality operators)5.7.1 정확한 결과를 내는데 있어 부동 소수점 계산을 기대하는 코드를 작성하지 않는다.
5.7.2 가상 함수 멤버로의 포인터가 오로지 nullptr로만 비교됨을 보장한다.
5.8 조건 연산자(Conditional operator)5.8.1 조건 연산자 (?:)를 하위 표현식(sub-expression)으로 사용하지 않는다.

6 문장(Statements)

6.1 선택문(Selection statements)6.1.1 복합문에서 선택문 또는 반복문의 몸체(body)를 둘러싼다.
6.1.2 다중(multi-way) 선택문을 가로지르는 모든 경로들을 명확하게 커버한다.
6.1.3 비어있지 않은 케이스문 블록이 다음 레이블로 떨어지지 않도록 보장한다.
6.1.4 스위치문이 디폴트 레이블과는 다른 적어도 두 개의 케이스 레이블을 가짐을 보장한다. 케이스 레이블 수가 2보다 적은 스위치문은 단일 if 문으로 더 자연스럽게 표현될 수 있음
6.2 반복문(Iteration statements)6.2.1 범위 기반 루프(a range-based loop)로서 오로지 요소 값(element values) 만을 사용하는 루프를 구현한다.
6.2.2 루프가 단일 루프 카운터(하나의 선택적 제어 변수)를 가지며 degenerate가 아님을 보장한다. degenerate 루프란 입력 시 루프가 무한이거나 또는 항상 첫 반복 후 종료되어 버리는 루프를 의미
6.2.3 루프에서 제어 변수 또는 카운터 변수를 한번 이상 변경하지 않는다.
6.2.4 for 표현식에서 오로지 for 루프 카운터만 변경시킨다.
6.3 점프문(Jump statements)6.3.1 점프문 또는 스위치문을 위한 레이블이 동일한 블록에서(또는 둘러싼 블록에서) 나중에 등장함을 보장한다.
6.3.2 non-void 리턴 타입을 가진 함수의 실행이 값을 가진 리턴문에서 끝남을 보장한다.
6.4 선언문(Declaration statement)6.4.1 변수 정의를 최대한 미룬다.

7 선언(Declarations)

7.1 지정자(Specifiers)7.1.1 각 식별자(identifier)를 별도 선언의 별도 라인 상에 선언한다. for 루프 초기화 문은 이 규칙에서 면제됨
7.1.2 가능하면 const를 사용한다. By-value 리턴 타입은 이 규칙에서 면제됨
7.1.3 선언에서 타입 지정자(type specifiers)를 non-type 지정자 앞에 놓지 않는다.
7.1.4 const 한정사(qualifiers) 또는 volatile 한정사를 이것이 적용되는 타입의 우측에 위치시킨다.
7.1.5 규모가 큰 함수를 인라인(inline)으로 하지 않는다.
7.1.6 스칼라 양과 표준 정수 타입을 나타내는데 클래스 타입 또는 typedef를 사용한다.
7.1.7 typename을 사용한 타입 명확화(disambiguation) 보다는 따라 붙는(trailing) 리턴 타입을 사용한다.
7.1.8 변수를 선언할 때 그 initializer 함수 호출과 동일한 타입을 가지도록 auto id = expr를 사용한다.
7.1.9 lambda의 리턴 타입을 노골적으로 명세하지 않는다.
7.1.10 컴파일 타임 상수(constants)와 관련된 어써션을 위해 static_assert를 사용한다.
7.2 열거형 선언(Enumeration declarations)7.2.1 명시적인 열거형 기반(an enumeration base)을 사용하고, 이것이 모든 열거자(enumerators)를 저장할 정도로 충분히 큰 것을 보장한다.
7.2.2 열거 선언에서 아무것도 초기화하지 않거나, 첫 번째 것만 초기화 하거나, 또는 모든 열거자를 초기화한다.
7.3 Namespaces7.3.1 using directive(즉, using namespace)를 사용하지 않는다.
7.4 링키지 명세(Linkage specifications)7.4.1 단일 번역 단위(a single translation unit)에서 사용되는 오브젝트, 함수, 또는 타입이 메인 소스 파일의 익명 네임스페이스(an unnamed namespace)에 정의되도록 보장한다.
7.4.2 다수의 번역 단위에서 사용되는 인라인 함수, 함수 템플리트, 또는 타입이 단일 헤더 파일에 정의되도록 보장한다.
7.4.3 다수의 번역 단위에서 사용되는 오브젝트 또는 함수가 단일 헤더 파일에 선언되도록 보장한다.
7.5 asm 선언7.5.1 asm 선언을 사용하지 않는다. 코드의 이식성(portability)을 제한하므로 인라인 어셈블리 사용을 피해야 함

8 정의(Definitions)

8.1 타입명(Type names)8.1.1 여러 수준의 포인터 간접참조(indirection)를 사용하지 않는다.
8.2 선언자의 의미(Meaning of declarators)8.2.1 모든 선언에서 패러미터명을 부재하도록 만들거나 또는 동일하게 만든다.
8.2.2 지나치게 많은 수의 패러미터로 함수 선언을 하지 않는다. 권고되는 함수 패러미터의 최대 수는 6이다.
8.2.3 작은 오브젝트는 단순한 값에 의한 복사 생성자(a copy constructor by value)로 전달한다.
8.2.4 const 참조에 의한 std::unique_ptr 패러미터를 전달하지 않는다.
8.3 함수 정의(Function definitions)8.3.1 지나치게 높은 McCabe 순환 복잡도(Cyclomatic Complexity)를 가진 함수를 작성하지 않는다. 이 메트릭 값이 10을 넘지 않을 것을 권고함
8.3.2 높은 정적 프로그램 경로 수(static program path count)를 가진 함수를 작성하지 않는다. 정적 프로그램 경로 수는 한 함수에서 비순환적(non-cyclic) 실행 경로들의 수를 의미하며, 단일 함수의 이것이 200을 넘지 않아야 한다.
8.3.3 디폴트 인자(default arguments)를 사용하지 않는다.
8.3.4 const로의 rvalue reference 타입 패러미터를 가진 =delete 함수를 정의한다.
8.4 초기화자(Initializers)8.4.1 유효하지 않는 오브젝트 또는 미결정 값(indeterminate value)을 가진 오브젝트에 접근하지 않는다.
8.4.2 중괄호로 둘러싸인 aggregate initializer가 aggregate 오브젝트의 레이아웃에 매치됨을 보장한다.

9 클래스(Classes)

9.1 멤버 함수(Member functions)9.1.1 this를 요구하지 않는 멤버 함수를 static으로 선언한다. 아니면 오브젝트의 외적으로 보이는 상태를 변경하지 않는 멤버 함수를 const로 선언한다.
9.1.2 가상 함수(a virtual function)를 오버라이딩할 때 디폴트 인자를 동일하게 만들거나 또는 부재하도록 만든다.
9.1.3 const 멤버 함수로부터 리턴된 클래스 데이터로 non-const 핸들을 반환하지 않는다.
9.1.4 멤버 함수보다 적은 접근성을 가지는 데이터로 non-const 핸들을 반환하는 멤버 함수를 작성하지 않는다. Non-const 연산자 []는 이 규칙으로부터 면제됨
9.1.5 최종 클래스(a final class)에 가상 함수를 도입하지 않는다.
9.2 비트 필드(Bit-fields)9.2.1 비트 필드를 분명한 무부호(unsigned) 인테그랄 타입 또는 열거형(enumeration) 타입으로 선언한다.

10 파생 클래스(Derived classes)

10.1 다수의 베이스 클래스(Multiple base classes)10.1.1 베이스 클래스 하위 오브젝트로의 접근에 있어서 노골적인 모호성 해소(disambiguation)가 필요하지 않음을 보장한다.
10.2 가상 함수(Virtual functions)10.2.1 가상 함수를 오버라이딩 할 때 override 특수 식별자를 사용한다.
10.3 추상 클래스(Abstract classes)10.3.1 파생 클래스가 많아 봐야 하나의 베이스 클래스(인터페이스 클래스가 아닌 것)를 가짐을 보장한다.

11 멤버 액세스 제어(Member access control)

11.1 접근 지정자(Access specifiers)11.1.1 모든 데이터 멤버를 private로 선언한다. extern “C” 블록에서 클래스 키 struct로 선언된 클래스 타입은 이 규칙에 의해 커버되지 않음
11.2 Friends11.2.1 friend 선언을 사용하지 않는다.

12 특수 멤버 함수(Special member functions)

12.1 전환(Conversions)12.1.1 암묵적인 사용자 정의(user defined) 전환을 선언하지 않는다.
12.2 소멸자(Destructors)12.2.1 베이스 클래스로써 사용되는 타입의 소멸자를 virtual, private, 또는 protected로 선언한다.
12.3 메모리 할당과 해제(Free store)12.3.1 operator new와 operator delete를 위한 오버로드를 정확하게 선언한다.
12.4 베이스 클래스와 멤버의 초기화(Initializing bases and members)12.4.1 오브젝트가 완전히 생성된 경우가 아니라면 오브젝트의 동적 타입을 사용하지 않는다.
12.4.2 생성자가 모든 베이스 클래스와 비정적(non-static) 데이터 멤버를 노골적으로(explicitly) 초기화함을 보장한다.
12.4.3 동일한 비정적 멤버를 위해 생성자에 비정적 데이터 멤버 초기화자(non static data member initializer)와 멤버 초기화자(member initializer) 양쪽 모두를 명세하지 않는다. (이 규칙이 Rule 12.4.2와 충돌하므로 이동/복사 생성자는 이 규칙에서 면제됨)
12.4.4 멤버들을 초기화 목록(an initialization list)에 그것들이 선언된 순서대로 작성한다.
12.4.5 코드 중복을 줄이기 위해 위임 생성자(delegating constructors)를 사용한다.
12.5 클래스 오브젝트의 복사 및 이동(Copying and moving class objects)12.5.1 구체 클래스(concrete classes)의 암묵적 특수 멤버 함수를 =default 또는 =delete로 명확하게 정의한다.
12.5.2 동작이 동일하다면 특수 멤버를 =default로 정의한다.
12.5.3 사용자 정의의 이동/복사 생성자가 오로지 베이스 및 멤버 오브젝트를 이동/복사함을 보장한다.
12.5.4 이동 생성자와 이동 할당 연산자를 noexcept로 선언한다.
12.5.5 moved-from 핸들을 이동 생성자의 리소스로 정확하게 리셋한다. 이것의 가장 흔한 예가 포인터 멤버로 nullptr를 할당하는 것이다.
12.5.6 복사/이동 할당 연산자를 구현하기 위해서 원자성의(atomic) non-throwing 교환 연산(swap operation)을 사용한다.
12.5.7 할당 연산자를 참조 한정자(ref-qualifier) &로 선언한다.
12.5.8 추상 클래스(abstract class)의 복사 할당 연산자를 protected로 만들거나, 또는 그것을 =delete로 정의한다.

13 오버로딩(Overloading)

13.1 오버로드 결정(Overload resolution)13.1.1 어떤 함수의 모든 오버로드가 해당 함수가 호출되어지는 위치에서 가시적임을 보장한다.
13.1.2 만약 호출가능한 함수들의 집합의 한 멤버가 보편적 참조 패러미터(a universal reference parameter)를 포함한다면, 이것이 모든 다른 멤버들을 위한 동일한 위치에 등장함을 보장한다.
13.2 오버로드된 연산자(Overloaded operators)13.2.1 특별한 시맨틱을 가진 연산자를 오버로드 하지 않는다.
13.2.2 오버로드된 2진 연산자의 리턴 타입이 상응하는 빌트인 연산자의 리턴 타입과 매치함을 보장한다.
13.2.3 오버로드된 2진 산술 연산자와 비트와이즈 연산자를 비멤버(non-members)로 선언한다.
13.2.4 첨자 연산자(operator[])를 오버로딩할 때는 const 버전과 non-const 버전 둘 다를 구현한다.
13.2.5 연산자들의 최소한의 집합을 구현하고, 이것들을 모든 다른 관련 연산자들을 구현하는데 사용한다.

14 템플리트(Templates)

14.1 템플리트 선언(Template declarations)14.1.1 ellipsis 템플리트 보다는 variadic 템플리트를 사용한다. 만약 variadic 템플리트 사용이 가능하지 않다면, 함수 오버로딩 또는 함수 호출 연쇄(function call chaining)가 고려되어야 한다.
14.2 템플리트 인스턴스화와 특수화(Template instantiation and specialization)14.2.1 템플리트 특수화를 특수화 하고자 하는 기본 템플리트(primary template)와 동일한 파일에 선언한다.
14.2.2 다른 템플리트로 오버로드 되는 함수 템플리트를 노골적으로 특수화하지 않는다.
14.2.3 노골적으로 인스턴스화된 템플리트를 extern으로 선언한다.

15 예외 처리(Exception handling)

15.1 예외 발생시키기(Throwing an exception)15.1.1 예외 발생에 오로지 std::exception의 인스턴스 만을 사용한다.
15.2 생성자와 소멸자(Constructors and destructors)15.2.1 소멸자에서 예외를 발생시키지 않는다.
15.3 예외 처리하기(Handling an exception)15.3.1 생성자/소멸자의 try 블록의 catch 핸들러로부터 비정적(non-static) 멤버에 액세스하지 않는다.
15.3.2 프로그램이 std::terminate 호출을 하는 결과를 낳지 않음을 보장한다.

16 전처리(Preprocessing)

16.1 소스 파일 포함(Source file inclusion)16.1.1 전처리기를 오로지 include guard를 구현하는데(즉, include guard를 가진 헤더 파일을 포함시키는데) 사용한다. 동일한 파일이 다수 포함(multiple inclusions)되는걸 막기 위해 include guard가 각 헤더 파일에 존재해야 함
16.1.2 #include 지시어(directives)에 주어지는 파일명에 경로 지정자(a path specifier)를 포함시키지 않는다.
16.1.3 #include 지시어(directives)에 있는 파일명을 파일 시스템 상의 그것과 매치시킨다. 일부 운영체제가 대소문자 구별이 없는 파일 시스템을 가지는데, 애초에 이런 시스템 상에서 개발된 코드가 대소문자를 구별하는 파일 시스템으로 이식될 때 성공적으로 컴파일 되지 못할 수도 있음
16.1.4 시스템 및 표준 라이브러리 헤더를 위해서 <> 괄호를 사용한다. 모든 다른 헤더를 위해서는 따옴표를 사용한다. 컴파일러 제공 헤더를 위해서는 #include <…>가 쓰이고, 사용자 제공 파일을 위해서는 #include “…”가 사용되는 것이 일반적인 관행
16.1.5 컴파일에 필요한 최소한의 수의 헤더를 직접적으로 포함시킨다. 필수적이지 않은 include directives의 존재가 대규모 코드 베이스의 컴파일을 상당히 느려지게 할 수 있으므로, 소스 파일 리팩터를 할 때에 포함된 헤더 목록을 검토하여 더 이상 필요하지 않은 include directives를 제거해야 함

17 표준 라이브러리(Standard library)

17.1 일반(General)17.1.1 std::vector<bool>을 사용하지 않는다.
17.2 C 표준 라이브러리17.2.1 C 표준 라이브러리가 사용되는 경우 그것을 wrapper로 감싼다. 이 wrapper가 미정의 동작(undefined behavior)이나 데이터 경쟁(data races)이 발생하지 않도록 보장 해 줌
17.3 일반 유틸리티 라이브러리(General utilities library)17.3.1 const 또는 const & 타입으로 선언된 오브젝트 상에 std::move를 사용하지 않는다.
17.3.2 보편 참조(universal references)를 포워딩하는데 std::forward를 사용한다.
17.3.3 std::forward로의 전달인자(argument)를 나중에 사용하지 않는다. 함수 호출에 사용된 인자들의 값 범주에 따라 std::forward가 패러미터 이동을 시킬 수도 또는 안 시킬 수도 있음. 패러미터의 값 범주가 lvalue 일 때는 패러미터의 변경이 호출자(caller)의 인자에 영향을 주며, rvalue인 경우는 std::forward 호출 후에 값이 미정(indeterminate)인 것으로 여겨져야 함
17.3.4 어레이 타입의 스마트 포인터를 생성하지 않는다.
17.3.5 std::array의 rvalue 참조(reference)를 생성하지 않는다.
17.4 컨테이너 라이브러리(Containers library)17.4.1 결과가 즉시 const iterator로 전환될 때 const 컨테이너 호출을 사용한다.
17.4.2 오브젝트를 제자리에(사용이 의도된 위치) 구축하는 API 호출을 사용한다.
17.5 알고리즘 라이브러리(Algorithms Library)17.5.1 std::remove, std::remove_if 또는 std::unique의 결과를 무시하지 않는다. 대개의 경우 올바른 동작은 이 결과를 std::erase로의 호출에서 첫 번째 피연산자로써 사용하는 것이다.

18 동시성(Concurrency)

18.1 일반(General)18.1.1 어떤 특정 플랫폼에 한정된 멀티쓰레딩 기능을 사용하지 않는다.
18.2 쓰레드(Threads)18.2.1 std::thread 대신에 high_integrity::thread를 사용한다.
18.2.2 쓰레드 간 공유되는 데이터로의 액세스를 단일 잠금(lock)을 사용하여 동기화한다.
18.2.3 쓰레드 간에 휘발성(volatile) 데이터를 공유하지 않는다.
18.2.4 더블 체킹 잠금 패턴(Double-Checked Locking pattern) 보다는 std::call_once를 사용한다.
18.3 상호 배제(Mutual Exclusion)18.3.1 어떤 잠금(lock)의 범위 내에서 동일 뮤텍스의 잠금이 되는 정적 경로가 없음을 보장한다(쓰레드가 자신이 이미 소유한 std::mutex를 잠그려 하는 시도를 피해야 함).
18.3.2 한 프로젝트에서 잠금의 포개어진(nesting) 순서가 방향성 비사이클 그래프(a Directed Acyclic Graph)를 형성함을 보장한다.
18.3.3 std::recursive_mutex를 사용하지 않는다.
18.3.4 std::lock_guard가 사용될 수 없을 때에만 std::unique_lock을 사용한다.  std::unique_lock 타입이 std::lock_guard에서는 가용하지 않은 추가적인 기능을 제공하므로 이를 사용하는데 있어 추가적인 비용이 따른다.
18.3.5 std::mutex의 멤버에 직접 액세스 하지 않는다. 뮤텍스 오브젝트는 오로지 그것을 소유한 std::lock_guard 또는 std::unique_lock 오브젝트에 의해서만 관리되어야 한다.
18.3.6 완화된 원자(relaxed atomics)를 사용하지 않는다.
18.4 조건 변수(Condition Variables)18.4.1 std::mutex 상에 std::condition_variable_any를 사용하지 않는다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다