The C++ Programming language의 조언들
2016. 5. 31. 12:42
1부 소개
1장 들어가며
-
구상(개념)을 함수, 클래스, 열거형 등을 통해서 직접적으로 코드에 표현한다.
-
코드를 깔끔하고 효율적으로 만들겠다는 목표를 세운다.
-
과도한 추상화를 하지 않는다.
-
라이브러리로 표현될 가능성이 있는, 갈끔하고 효율적인 추상화를 제공하는 데 설계의 초점을 맞춘다.
-
매개변수화나 클래스 계층 구조 등을 통해 아이디어 사이의 관게를 직접적으로 코드에 표현한다.
-
독립적인 아이디어는 코드에 개별적으로 표현한다. 예를 들어 클래스 간의 상호 의존성은 피한다.
-
C++는 객체지향적이지만은 않다.
-
C++는 일반화 프로그래밍을 위한 것만은 아니다.
-
정적으로 체크할 수 있는 해결책을 우선 사용한다.
-
자원은 명시적으로 표시한다.
-
간단한 아이디어는 간단히 표현한다.
-
밑바닥에서부터 모든 것을 만들려고 애쓰지 말고 라이브러리, 그 중에서도 특히 표준 라이브러리를 활용한다.
-
타입이 풍부한 프로그래밍 스타일을 활용한다.
-
하위 수준의 코드가 반드시 효율적이지는 않다. 성능 문제 때문에 클래스, 템플릿, 표준, 라이브러리 구성 요소를 사용하는 걸 주저하지 않는다.
-
데이터가 불변속성을 갖고 있다면 캡슐화한다.
-
C++는 단지 몇 가지 확장 기능을 가진 C가 아니다.
2장 C++ 둘러보기 : 기초
- 당황하지 말자! 시간이 지나면 알게 된다.
- 훌륭한 프로그램을 작성하기 위해 C++의 세부 사항까지 전부 알 필요는 없다.
- 언어 기능이 아니라 프로그래밍 기법에 집중하자.
3장 C++ 둘러보기 : 추상화 메커니즘
- 아이디어를 직접적으로 코드에 표현한다.
- 응용 개념을 직접적으로 코드에 표현해주는 클래스를 정의한다.
- 구체 클래스를 이용해서 간단한 개념과 성능이 중요한 구성 요소를 표현한다.
- '무방비'의 new와 delete 연산은 피한다.
- 자원 핸들과 RAII를 이용해서 자원을 관리한다.
- 인터페이스와 구현의 완벽한 분리가 필요할 때는 추상 클래스를 인터페이스로 활용한다.
- 클래스 계층 구조를 이용해서 내재적인 계층 구조를 가진 개념을 표현한다.
- 클래스 계층 구조를 설계할 때는 구현 상속과 인터페이스 상속을 구분한다.
- 객체의 복사, 이동, 소멸을 통제한다.
- 컨테이너는 값으로 반환한다(효율성이 중요하다면 move를 이용한다.)
- 강력한 자원 안전성을 제공한다. 즉 자원이라고 여겨지는 것이라면 절대로 누출시키지 않는다.
- 자원 핸들 템플릿으로 정의된 컨테이너를 이용해서 동일한 타입의 값으로 이뤄진 집합을 보관한다.
- 함수 템플릿을 이용해서 일반적인 알고리즘을 표현한다.
- 람다를 비롯한 함수 객체를 이용해서 정책과 작동을 표현한다.
- 타입 및 템플릿 별칭을 이용해서 유사한 타입이나 구현에 따라 변할 수 있는 타입에 대해 동일한 이름을 부여한다.
4장 C++ 둘러보기 : 컨테이너와 알고리즘
- 바퀴를 다시 만들 필요는 없다. 라이브러리를 활용한다.
- 선택 가능할 경우에는 다른 라이브러리보다 표준 라이브러리를 우선 사용한다.
- 표준 라이브러리가 만능이라고 생각하지는 말기 바란다.
- 사용하려는 기능에 대한 헤더를 #include 하는 것을 잊지 않는다.
- 표준 라이브러리 기능은 std 네임스페이스 범위로 정의된다는 점을 명심한다.
- C 스타일 문자열보다 string을 우선 사용한다.
- istream은 타입을 구분하고, 타입 안전성이 있으며, 확장성이 있다.
- T[] 보다 vector<T>, map<K, T>, unordered_map<K,T>를 우선 사용한다.
- 표준 커네이너와 그들의 장단점을 파악한다.
- vector를 기본 컨테이너로 활용한다.
- 가급적 간결한 데이터 구조를 사용한다.
- 의심스럽다면 범위 체크 벡터를 사용한다.
- push_back() 또는 back_inserter()를 사용해서 컨테이너에 원소를 추가한다.
- 배열에 realloc()을 사용하지 않고, vector에 push_back()을 사용한다.
- 일반적인 예외는 main()에서 잡는다.
- 표준 알고리즘을 파악해서 직접 만든 루프보다는 표준 알고리즘을 우선 사용한다.
- 반복자가 장황해지면 컨테이너 알고리즘을 정의한다.
- 완전한 컨테이너에 대해서는 범위기반 for 루프를 사용한다.
5장 C++ 둘러보기 : 병행성과 유틸리티
- 자원 핸들을 이용해서 자원을 관리한다(RAII)
- unique_ptr을 이용해서 다형성 타입의 객체를 참조한다.
- shared_ptr을 이용해서 공유 객체를 참조한다.
- 병행성에 대해서는 타입 안전적인 메커니즘을 사용한다.
- 공유 데이터의 사용은 최소화한다.
- 심사숙고나 실제적인 측정 없이 '효율성'이라는 이유 때문에 통신을 위해 공유 데이터를 선택하지 말기 바란다.
- 스레드보다는 병행 태스크의 관점에서 생각한다.
- 라이브러리는 규모가 크거나 복잡하지 않아도 쓸모가 있을 수 있다.
- 효율성에 대해서 주장하기 전에 자신의 프로그램을 측정해봐야 한다.
- 타입 속성에 명시적으로 의존하는 코드를 작성할 수 있다.
- 간단한 패턴 매칭을 위해서는 정규 표현식을 사용한다.
- 언어만 사용해서 본격적인 수치 계산을 처리하고 시도하지 않는다. 라이브러리를 활용한다.
- 수치 타입의 속성은 numeric_limits를 통해 접근할 수 있다.
2부 기본 기능
6장 타입과 선언
- 언어 정의 이슈에 대한 최종 결정 사항은 ISO C++ 표준을 참고한다.
- 지정되지 않거나 정의되지 않은 동작은 삼간다.
- 구현별 정의에 따르는 동작에 반드시 의존하는 코드는 격리한다.
- 문자의 수치 값에 대한 불필요한 추정은 삼간다.
- 0으로 시작하는 정수는 7진수라는 점을 명심한다.
- '마법의 상수'는 피한다.
- 정수의 크기에 대한 불필요한 추정은 삼간다.
- 부동소수점 타입의 범위와 정밀도에 대한 불필요한 추정은 삼간다.
- signed char나 unsigned char보다 일반적인 char가 낫다.
- 부호 있는 타입과 부호 없는 타입 간의 변환은 조심한다.
- 하나의 선언에서는 하나의 이름만 선언한다.
- 통상적인 이름과 지역 이름은 짧게 만들고, 자주 사용되지 않는 이름과 비지역 이름은 길게 만든다.
- 이름은 비슷하게 만들지 않는다.
- 타입보다는 의미에 맞춰 객체에 이름을 붙인다.
- 일관된 명명 규칙을 준수한다.
- ALL_CAPS 이름을 피한다.
- 유효 범위는 가급적 좁게 만든다.
- 하나의 유효 범위 내에서뿐만 아니라 이를 둘러싼 바깥 유효 범위 내에서도 같은 이름은 쓰지 않는다.
- 이름이 있는 타입을 가진 선언에 대해서는 {} 초기화 식 문법을 사용하는 것이 좋다.
- auto를 사용하는 선언에서는 초기화에 = 문법을 사용하는 것이 좋다.
- 초기화되지 않은 변수는 삼간다.
- 기본 제공 타입이 변경될 수 있는 값을 표시하는 데 사용된 경우에는 기본 제공 타입에 의미 있는 이름을 붙이기 위해 별칭을 사용한다.
- 별칭은 다른 타입을 다른 이름으로 표시하고 싶을 때 사용한다. 새로운 타입을 정의하고 싶다면 열거형과 클래스를 이용한다.
7장 포인터, 배열, 참조
- 포인터는 간결하고 직관적으로 사용한다.
- 허용되지 않은 포인터 연산은 삼간다.
- 경계 배열 너머에 쓰지 않도록 조심한다.
- 다차원 배열을 피한다. 대신 적절한 컨테이너를 정의한다.
- 0이나 NULL 보다 nullptr을 이용한다.
- 기본 제공 C스타일의 배열보다 컨테이너(vector, array, valarray 등)를 이용한다.
- 0으로 종료되는 char의 배열보다 string을 이용한다.
- 백슬래시를 복잡하게 사용하는 문자열 리터럴에는 원시 문자열을 이용한다.
- 보통의 참조자 인자보다 const 참조자 인자를 선택한다.
- 포워딩과 이동 의미 구조에만 우변 값 참조자를 이용한다.
- 소유권을 표시하는 포인터는 핸들 클래스 내부에 둔다.
- 저수준의 코드를 제외하고는 void*를 삼간다.
- 인터페이스에서 불변성을 표헌하기 위해 const 포인터와 const 참조자를 이용한다.
- '객체 없음'이 타당한 선택인 경우를 제외하고는 포인터보다는 참조자를 인자로 선택한다.
8장 구조체, 공용체, 열거형
- 데이터 용량의 최소화가 필요한 경우에는 작은 멤버보다 좀 더 큰 멤버를 앞에두는 구조로 만든다.
- 하드웨어에 노출되는 데이터 구조를 표시하려면 비트필드를 활용한다.
- 여러 개의 값을 하나의 바이트에 구겨 넣는 단순한 방식으로 메모리 소비를 최적화하려고 시도하지 말기 바란다.
- union은 메모리 공간 절약에 활용하고 타입 변환에는 이용하지 않는다.
- 열거형을 이용해서 이름 있는 상수 집합을 표시한다.
- 예기치 않은 결과를 최소화하기 위해 '일반' enum보다 enum class 를 사용한다.
- 안전하고 간편한 활용을 위해 열거형에 대해 연산을 정의한다.
9장 문장
- 초기화할 수 있는 값이 생기기 전에는 변수 선언을 삼간다.
- 가능하다면 if문보다 switch문을 사용한다.
- 가능하다면 for문보다 범위 기반 for문을 사용한다.
- 명확한 루프 변수가 있는 경우에는 while문보다 for문을 사용한다.
- 명확한 루프 변수가 없는 경우에는 for문보다 while문을 사용한다.
- do문은 피한다.
- goto문은 피한다.
- 주석은 간결하게 유지한다.
- 코드를 보면 명확히 알 수 있는 사항은 주석에 적지 않는다.
- 주석에 의도를 설명한다.
- 일관된 들여쓰기 스타일을 유지한다.
10장 표현식
- 다른 라이브러리나 '직접 짠 코드'보다 표준 라이브러리를 사용한다.
- 문자 수준의 입력은 어쩔 수 없을 때만 사용한다.
- 읽기를 수행할 때는 언제나 형식이 맞지 않는 입력을 고려한다.
- 언어 기능을 직접적으로 이용하기보다는 적합한 추상화를 우선 사용한다.
- 복잡한 표현식을 피한다.
- 연산자 우선순위에 대해 의심이 생긴다면 괄호를 친다.
- 정의되지 않은 평가 순서를 가진 표현식은 피한다.
- 축소 변환은 피한다.
- '매직 상수'를 피하기 위해 기호 상수를 정의한다.
11장 선택 연산
- 접미사 ++보다 접두사 ++를 우선 사용한다.
- 자원 핸들을 이용해서 누출, 때 이른 소멸, 이중 소멸을 피한다.
- 꼭 그럴 필요가 없다면 객체를 자유 저장 공간에 넣지 않는다. 유효 범위를 가진 변수를 우선 사용한다.
- '무방비의 new'나 '무방비의 delete'는 피한다.
- RAII를 사용한다.
- 연산이 주석을 필요로 하는 경우라면 람다보다 이름을 가진 함수 객체를 우선 사용한다.
- 연산이 범용적으로 유용하다면 람다보다 이름을 가진 함수 객체를 우선 사용한다.
- 람다는 짧게 만든다.
- 유지 보수성과 정확성을 위해 참조자에 의한 캡처에 대해서는 신중을 기한다.
- 컴파일러가 람다의 반환 타입을 추론하게 만든다.
- 생성에는 T{e} 표기법을 사용한다.
- 명시적 타입 변환(캐스트)는 피한다.
- 명시적 타입 변환이 필요한 경우에는 이름 있는 캐스트를 우선 사용한다.
- 수치 타입간의 변환에는 narrow_cast<>()같은 런타임 체크 캐스트의 사용을 고려한다.
12장 함수
- 의미 있는 연산은 신중하게 이름을 붙인 함수로 '패키지'화한다.
- 함수는 단일한 논리 연산을 수행해야 한다.
- 함수는 짧게 만든다.
- 지역 변수를 가리키는 포인터나 지역 변수에 대한 참조자는 반환하지 않는다.
- 어떤 함수가 컴파일 타임에 평가되어야 한다면 constexpr로 선언한다.
- 어떤 함수가 반환할 수 없다면 [[noreturn]]으로 표시한다.
- 작은 객체에 대해서는 값에 의한 전달을 사용한다.
- 변경할 필요가 없는 큰 값을 전달할 때는 const 참조자에 의한 전달을 사용한다.
- 인자를 통해 객체를 변경하기보다는 return 값으로서 결과를 반환한다.
- 이동과 전달을 구현하려면 우변 값 참조자를 사용한다.
- '객체 없음'이 유효한 대안인 경우에는 포인터를 전달한다.
- 그럴 필요가 있을 때만 const가 아닌 참조자에 의한 전달을 사용한다.
- const를 광범위하고 일관성 있게 사용한다.
- char*나 const char* 인자는 C스타일 문자열을 가리킨다고 가정한다.
- 배열을 포인터로 전달하는 것은 피한다.
- 길이를 모르는 균일한 리스트는 initializer_list<T>로 전달한다.
- 지정되지 않은 인자의 개수(...)는 피한다.
- 함수가 서로 다른 타입에 개념적으로 동일한 작업을 수행할 때는 오버로딩을 활용한다.
- 정수에 대해 오버로딩을 할 경우에는 일반적인 모호성을 제거해 주는 함수를 제공한다.
- 함수에 대해 선행 조건과 후행 조건을 지정한다.
- 함수를 가리키는 포인터보다 함수 객체와 가상 함수를 우선적으로 사용한다.
- 매크로는 피한다.
- 매크로를 사용해야 한돠면 대문자를 많이 가진 이상해 보이는 이름을 사용한다.
13장 예외 처리
- 설계 초기에 오류 처리 전략을 개발한다.
- 주어진 작업을 수행할 수 없다는 점을 나타내려면 예외를 던진다.
- 오류 처리에 예외를 이용한다.
- 예외로는 목적을 가지고 설계한 사용자 정의 타입을 사용한다.
- 어떤 이유에서 예외를 사용할 수 없다면 그것을 흉내 낸다.
- 계층적인 오류 처리를 사용한다.
- 오류 처리의 개별적인 부분들은 간단하게 만든다.
- 모든 함수에서 모든 예외를 잡으려고 시도하지 않는다.
- 기본적인 보장은 항상 제공한다.
- 그러지 않을 이유가 없다면 강력한 보장을 제공한다.
- 생성자 내에서 획득한 모든 자원은 해당 생성자에서 예외를 던지기 전에 반드시 해제한다.
- 좀 더 지역적인 제어로 충분한 경우에는 예외를 사용하지 않는ㄴ다.
- '자원 획득은 초기화' 기법을 이용해서 자원을 관리한다.
- try 블록의 사용은 최소화한다.
- 모든 프로그램이 예외 안전적일 필요는 없다.
- 'RAII'와 예외 핸들러를 이용해서 불변속성을 관리한다.
- 덜 구조화된 finally보다는 적합한 자원 핸들을 우선 사용한다.
- 불변속성을 기반으로 오류 처리 전략을 설계한다.
- 컴파일 타임에 체크될 수 있는 것이라면 대개는 컴파일 타임에 체크하는 것이 최선이다.
- 다양한 수준의 체크/강제를 수용할 수 있게 오류 처리 전략을 설계한다.
- 함수가 예외를 던질 수 없다면 noexcept로 선언한다.
- 예외 지정은 사용하지 않는다.
- 계층 구조의 일부분이 될 수 있는 예외는 참조자로 잡는다.
- 모든 예외가 exception 클래스에서 파생된다고 가정하지 않는다.
- main()이 모든 예외를 붙잡고 보고하게 만든다.
- 대체할 것이 준비되기 전에 정보를 소멸시키지 않는다.
- 대입문에서 에외를 던지기 전에 피연산자는 유효한 상태여야 한다.
- 소멸자에서는 예외를 절대로 던지지 않는다.
- 통상적인 코드와 오류 처리 코드는 분리한다.
- new에 의해 할당된 다음 예외가 일어난 경우에 해제되지 않는 메모리로 인해서 발생하는 메모리 누출에 주의한다.
- 함수에 의해 던져질 수 있는 모든 예외는 던져질 것이라고 가정한다.
- 라으버리는 일방적으로 프로그램을 종료하지 말아야 한다. 대신 예외를 던지고 호출자가 결정하게 한다.
- 라이브러리는 최종 사용자를 대상으로 한 진단적 출력을 내보내지 말아야 한다. 대신 예외를 던지고 호출자가 결정하게 한다.
14장 네임스페이스
- 네임스페이스를 이용해서 논리적 구조를 표현한다.
- main()을 제외한 비지역적 이름은 모두 어떤 네임스페이스에 넣는다.
- 무관한 네임스페이스에 불필요하게 접근을 하지 않고도 편리하게 이용할 수 있도록 네임스페이스를 설계한다.
- 매우 짧은 이름은 네임스페이스에 쓰지 않는다.
- 필요하다면 긴 네임스페이스 이름을 줄이기 위해 네임스페이스 별칭을 사용한다.
- 네임스페이스 사용자에게 심각한 표기적 부담을 지우지 않도록 한다.
- 인터페이스와 구현에 대해서는 별개의 네임스페이스를 사용한다.
- 네임스페이스 멤버를 정의할 때는 Namespace::member 표기를 사용한다.
- inline 네임스페이스를 이용해서 버전 관리를 지원한다.
- using 지시자는 전환, 기반 라이브러리(std등)으로 사용하거나 지역 유효 범위 내에서 사용한다.
- 헤더 파일에 using 지시자를 넣지 않는다.
15장 소스 파일과 프로그램
- 헤더 파일을 이용해서 인터페이스를 표시하고 논리적 구조를 강조한다.
- 그것의 함수를 구현하는 소스 파일 안에 헤더를 #include 한다.
- 서로 다른 해석 단윙에서 같은 이름을 갖거나 비슷하지만 다른 뜻을 가진 전역 객체를 정의하지 않는다.
- 헤더 안에서 인라인이 아닌 함수는 피한다.
- 전역 유효 범위와 네임스페이스 안에서만 #include를 사용한다.
- 완전한 선언만을 #include 한다.
- 인클루드 가드를 사용한다.
- 전역 이름을 피하기 위해 네임스페이스 안에 C헤더를 #include 한다.
- 헤더는 자기 완결적으로 만든다.
- 사용자 인터페이스와 구현자 인터페이슬르 구분한다.
- 평균적인 사용자의 인터페이스와 전문 사용자의 인터페이스를 구분한다.
- C++로 작성되지 않은 프로그램의 일부로 사용될 예정인 코드에서 런타임 초기화를 요구하는 비지역 객체는 피한다.
3부 추상화 메커니즘
16장 클래스
- 클래스로 개념을 표현한다.
- 클래스의 인터페이스는 구현과 분리한다.
- 정말로 데이터뿐이고 데이터 멤버에 대해 의미 있는 불변속성이 없는 경우에만 공개 데이터를 사용한다.
- 객체의 초기화를 처리하기 위해 생성자를 정의한다.
- 기본적으로 단일 인자 생성자는 explicit으로 선언한다.
- 자신의 객체를 변경하지 않는 멤버 함수는 const로 선언한다.
- 구체 타입은 가장 간단한 종류의 클래스다. 적용 가능한 경우에는 좀 더 복잡한 클래스나 순수한 데이터 구조보다 구체 타입을 우선 사용한다.
- 클래스의 표현에 직접적인 접근을 필요로 하는 경우에만 함수를 멤버로 만든다.
- 네임스페이스를 이용해서 클래스와 그것의 보조 함수의 연관관계를 명시적으로 만든다.
- 클래스 내부 초기화 식을 이용해서 생성자의 중복을 피한다.
- 클래스의 표현에 접근해야 하지만 구체적인 객체에 대해 호출될 필요가 없는 함수는 static 멤버 함수로 만든다.
17장 생성, 마무리, 복사와 이동
- 생성자, 대입, 소멸자는 서로 잘 어울리는 연산 집합을 이루게 설계한다.
- 생성자를 사용해서 클래스에 대한 불변속성을 구축한다.
- 생성자가 자원을 획득하면 그것의 클래스는 자원을 해제하기 위한 소멸자를 필요로 한다.
- 클래스가 가상 함수를 갖고 있다면 가상 소멸자를 필요로 한다.
- 클래스가 생성자를 갖고 있지 않다면 멤버 단위 초기화에 의해 초기화될 수 있다.
- 가급적 =와 () 초기화보다 {}초기화를 사용한다.
- '자연스러운' 기본 값이 있는 경우에만 클래스에 기본 생성자를 제공한다.
- 어떤 클래스가 컨테이너라면 초기화 식 리스트 생성자를 제공한다.
- 멤버와 기반 클래스는 선언 순서대로 초기화한다.
- 어떤 클래스가 참조자 멤버를 갖고 있다면 아마도 복사 연산을 필요로 할 것이다.
- 생성자 내에서는 가급적 대입보다 멤버 초기화를 사용한다.
- 클래스 변환 초기화 식을 이용해서 기본 값을 제공한다.
- 어떤 클래스가 자원 핸들이라면 아마도 복사와 이동 연산을 필요로 할 것이다.
- 복사 생성자를 작성할 때는 복사돼야 할 모든 멤버를 빠짐없이 복사하게 유의한다.
- 복사 연산은 등가성과 독립성을 제공해야 한다.
- 뒤엉킨 데이터 구조에 유의한다.
- 가급적 얉은 복사보다는 이동 의미 구조나 쓰기 시 복사를 사용한다.
- 어떤 클래스가 기반 클래스로 사용된다면 복사 손실이 일어나지 않게 보호한다.
- 어떤 클래스가 복사 연산이나 소멸자를 필요로 한다면 아마도 생성자, 소멸자, 복사 대입, 복사 생성자를 필요로 할 것이다ㅏ.
- 어떤 클래스가 포인터 멤버를 갖고 있다면 아마도 소멸자 및 기본이 아닌 복사 연산을 필요로 할 것이다.
- 어떤 클래스가 자원 핸들이라면 생성자, 소멸자 및 기본이 아닌 복사 연산을 필요로 할 것이다.
- 기본 생성자, 대입 또는 소멸자가 적절하다면 컴파일러가 그것들을 생성하게 해야한다.
- 불변속성에 대해서는 명시적이어야 한다. 생성자를 사용해서 그것들을 구축하고 대입을 사용해서 그것들을 유지한다.
- 복사 대입이 자기 대입에 대해서도 안전하게끔 만들어야 한다.
- 클래스에 새로운 멤버를 추가할 때는 해당 멤버를 초기화하기 위해 갱신되어야 하는 사용자 정의 생성자가 있는지 확인한다.
18장 연산자 오버로딩
- 주로 관용적인 사용법을 흉내 낼 목적으로 연산자를 정의한다.생성자가 자원을
- 기본 복사가 어떤 타입에 적합하지 않다면 복사를 재정의하든지 금지한다.
- 큰 피연산자에 대해서는 const 참조자 인자 타입을 사용한다.
- 큰 결과에 대해서는 이동 생성자를 이용한다.
- 표현에 접근해야 하는 연산에 대해서는 비멤버보다 멤버 함수를 우선 사용한다.
- 표현에 접근해야 할 필요가 없는 연산에 대해서는 멤버 함수보다 비멤버 함수를 우선 사용한다.
- 네임스페이스를 이용해서 클래스와 보조 함수를 연결한다.
- 대칭적인 연산자에 대해서는 비멤버 함수를 사용한다.
- 좌변 값과 왼쪽 피연산자를 필요로 하는 연산자를 표현하기 위해서는 멤버 함수를 사용한다.
- 관용적인 표기법을 흉내 내려면 사용자 정의 리터럴을 사용한다.
- 클래스의 기본 의미 구조가 요구하는 경우에만 데이터 멤버에 대한 Set과 Get()함수를 제공한다.
- 암시적 변환의 도입은 신중히 해야한다.
- 값을 소멸시키는 변환은 피한다.
- 생성자와 변환 연산자 양쪽 모두와 동일한 변환은 정의하지 않는다.
19장 특수 연산자
- operator[]()는 첨자 연산과 단일 값 기반의 선택에 사용한다.
- operator()()는 호출 의미 구조, 첨자 연산, 다중 값 기반의 선택에 사용한다.
- operator->()는 '스마트 포인터'를 역참조하는 데 사용한다.
- 가급적 후위형 ++보다 전위형 ++를 사용한다. (전위형은 참조를 반환, 후위형은 새 객체 반환)
- 정말로 그래야 하는 경우에만 전역 operator new()와 operator delete()를 정의한다.
- 멤버 operator new()와 멤버 operator delete()를 정의해서 특정 클래스 또는 클래스의 계층 구조 객체에 대한 할당 및 할당 해제를 제어한다.
- 사용자 정의 리터럴을 사용해서 관용적인 표기법을 흉내 낸다.
- 선택적인 사용을 허용하려면 리터럴 연산자를 별도의 네임스페이스에 넣는다.
- 특수하지 않은 용도에 대해서는 직접 연습한 결과물보다 표준 string을 사용하기 바란다.
- 클래스 표현에 접근해야 하는 비멤버 함수가 필요하다면 프렌드 함수를 이용한다.
- 클래스의 표현에 대한 접근을 허용하는 데는 가급적 프렌드 함수보다는 멤버 함수를 이용하기 바란다.
20장 파생 클래스
- 타입 필드는 피한다.
- 다형적 객체는 포인터와 참조자를 통해 접근한다.
- 추상 클래스를 이용해서 깔끔한 인터페이스의 제공에 설계의 초점을 맞춘다.
- override를 이용해서 대규모 클래스 계층 구조에서 재정의를 명시적으로 만든다.
- final은 절제해서 사용한다.
- 추상 클래스를 이용해서 인터페이스를 지정한다.
- 추상 클래스를 이용해서 구현 세부 사항을 인터페이스와 분리한다.
- 가상 함수를 가진 클래스는 가상 소멸자를 가져야 한다.
- 추상 클래스는 대개 생성자를 필요로 하지 않는다.
- 구현 세부 사항에 대해서는 가급적 private 멤버를 사용한다.
- 인터페이스에 대해서는 가급적 public 멤버를 사용한다.
- protected 멤버는 정말 필요할 때만 조심해서 사용한다.
- 데이터 멤버는 protected로 선언하지 않는다.
21장 클래스 계층 구조
- new를 이용해서 생성된 객체의 delete를 잊지 않기 위해서는 unique_ptr이나 shared_ptr을 이용한다.
- 인터페이스로 사용할 기반 클래스에는 데이터 멤버를 넣지 않는다.
- 추상 클래스를 이용해서 인터페이스를 표현한다.
- 추상 클래스에는 가상 소멸자를 제공해서 올바른 마무리를 보장한다.
- 대규모 클래스 계층 구조에서는 override를 사용해서 재정의를 명시적으로 만든다.
- 추상 클래스를 사용해서 인터페이스 상속을 지원한다.
- 데이터 멤버를 가진 기반 클래스를 이용해서 구현 상속을 지원한다.
- 통상적인 다중 상속을 이용해서 기능의 합집합을 표현한다.
- 다중 상속을 이용해서 인터페이스에서 구현을 분리한다.
- 가상 기반 클래스를 이용해서 계층 구조 내에서 일부 클래스에 공통된 뭔가를 표현한다.
'프로그래밍 > C, C++ 공부' 카테고리의 다른 글
이중배열, N중배열 (0) | 2018.02.17 |
---|---|
배열의 이름 (0) | 2018.02.16 |
C/C++의 컴파일러는 왜 배열의 크기를 알아야 할까? (0) | 2018.02.16 |
정수의 자릿수 구하기, 한 자리씩 판별하기 (0) | 2018.02.16 |
for문을 사용할 때 (0) | 2018.02.15 |