2016년 4월 7일 목요일

Effective C++ (ECPP) 'Chapter 1 - C++에 왔으면 C++의 법을 따릅시다'

  • 항목 1 : C++를 언어들의 연합체로 바라보는 안목은 필수
 C++는 단일 언어로 보지 않고 여러개의 하위 언어를 제공한다고 봐야합니다. C++는 한 가지 프로그래밍 규칙인 통합 언어가 아니라 하위 언어들의 연합체입니다. C++를 사용한 효과적인 프로그래밍 규칙은 C++의 어떤 부분을 사용하느냐에 따라 달라집니다. C++의 하위 언어는 다음과 같이 4가지 입니다.
- C : template, exception, overloading 등등이 없는 C
- 객체 지향 개념의 C++ : class, capsulation, inheritance, polymorphism, virtual function 등의 class를 쓰는 c
- template c++ : template metaprogramming(TMP)
- STL : container, iterator, algorithm, function object가 서로 얽혀 돌아가는 규약


  • 항목 2 : #define을 쓰려거든 const, enum, inline을 떠올리자
1. #define은 컴파일러에게 소스코드가 넘어가기 전에 선행 처리자가 숫자 상수로 바꾸어 버리기 때문에, #define에 사용한 이름이  컴파일러가 쓰는 기호 테이블에 들어가지 않는다. 숫자 상수로 바뀐 부분에서 에러가 발생한다면 에러의 원인을 찾기 힘들 것이다. 따라서 매크로 대신 const 상수를 사용한다.
 #define KYS  1   =>  const int KYS = 1;


2. 정수 타입의 정적 클래스 상수에 대한 클래스 내 초기화를 금지하는 구식 컴파일러일 때, class를 컴파일 하는 도중에 클래스 상수의 값이 필요한 경우는 enum을 사용해야한다. 
(배열 멤버를 선언하는 경우가 대표적이다.)
class A{
private:
 enum { num = 5};
 int tmpArr[num];
}
이를 enum hack이라고 말한다.

3. 함수처럼 쓰이는 매크로를 만들려면 #define대신 inline을 사용한다. #define으로 매크로 함수를 정의할 경우, 인자마다 괄호를 씌어주어야 하며, 골치 아픈 일이 많이 발생할 여지가 있다. inline을 사용할 경우 인자마다 괄호를 사용할 필요가 없고, #define에서 발생할 문제들이 해결되며 함수의 유효범위 및 접근규칙도 그대로 따라 갑니다. 


  • 항목 3 : 낌새만 보이면 const를 들이대 보자!
1. const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는 데 도움을 줍니다.  const는 어떤 유효범위에 있는 객체에도 붙을 수 있으며, 함수 매개변수 및 반환 타입, 멤버 함수에도 붙을 수 있습니다.

2. 컴파일러 쪽에서 보면 bitwise constness를 지켜야 하지만, 사용자는 logical constness를 사용해서 프로그래밍 해야 합니다.

3. 상수 멤버 및 비 상수 멤버 함수가 기능적으로 똑같게 구현되어 있을 경우에는 코드 중복을 피하는 것이 좋은데, 이때 비상수 버전이 상수 버전을 호출하도록 만들어야 합니다. (상수버전이 비상수 버전을 호출하는 경우는 안정성에 문제가 생깁니다.)

ex)
const char& operator[](std::size_t position)const {
return text[position];
}
char& operator[](std::size_t position) {
return
const_cast<char&>(
static_cast<const TextBlock&>(*this)[position]
);
}



  • 항목 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화하자

1. primitive type 객체는 직접 초기화합니다. primitivie type 객체는 경우에 따라 저절로 초기화 되기도 하기 안 될 수도 있기 떄문입니다.

2. 생성자에는 데이터 멤버에 대하여 생성자 함수 내부에 대입문을 사용하여 멤버를 초기화 하지 말고 멤버 초기화 리스트를 사용하는게 좋다.
 대입문을 사용할 경우, 데이터 멤버가 사용자 객체 일 때, 대입 하기 전에 기본 생성자를 호출하여 기본 값들로 초기화한다. 그 후에 대입문을 통해 사용자 객체 멤버에 대입을 통한 값을 저장한다. 즉, 기본 생성자를 호출하는 쓸데 없는 작업이 발생한다.
 멤버 초기화 리스트는 해당 멤버 값에 대한 생성자 함수를 호출 하기 때문에 기본생성자를 호출하는 일은 발생하지 않는다. 따라서 멤버 초기화 리스트를 사용하는 것이 더 효율적이다.

3. 여러 translate unit에 있는 non-local static object들의 초기화 순서 문제는 피해서 설계해야 한다. 즉, non-local static object를 local static object로 바꾸면 된다.
 => 여러 소스코드에 각각 객체들이 생성한다고 가정한다. 다른 소스코드의 객체를 사용하려 할 때, 해당 소스코드의 객체가 초기화 되지도 않은 상태에서 해당 객체를 사용할 경우 문제가 발생한다. 이러한 경우를 방지하기 위해 local static object(함수 안에 static으로 객체를 생성한 후 반환) 방식을 사용하여 해당 객체를 사용하려 할 때, 함수를 호출하여 그 함수가 객체를 생성 및 초기화한다. 그 후에 함수는 해당 객체를 반환하여 그 객체를 사용하는 방식이다.


◈ local static object
- 함수 안에 static으로 선언된 객체

◈ non-local static object
- 전역 객체
- 네임스페이스 유효범위에서 정의된 객체
- 클래스 안에서 static으로 선언된 객체
- 파일 유효범위에서 static으로 정의된 객체

댓글 없음:

댓글 쓰기