Special Hot Alien ?! ;-)

Effective C++ summary

25 Aug 2016

1.C++ = C (+) 객체지향의 C++ (+) 템플릿 C++ (+) STL 인 연합체로 바라 보자.

2.#define 을 쓰려거든 const, enum, inline을 사용해라.

3.낌새만 보이면 const를 사용하자.

4.객체를 사용하기전에 반드시 그 객체를 초기화 하자.

멤버 초기화 리스트에 멤버들의 순서대로 맞춰 초기화한다.

5.C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우다

(빈 클래스를 선언해도 생성자, 소멸자, 복사생성자, 복사대입연산자가 컴파일러에 의해 생성된다.)

6.컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자.

금해 버리고싶은 멤버 함수를 private로 선언한 후에 구현하지 않는다. boost library의 noncopyable도 좋은 예이다.

7.다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자 (virtual)로 선언하자.

그리하지 않으면 기본 클래스 포인터를 통해 파생 클래스 객체가 삭제될 때 부분 소멸(partially destroyed)이 된다. STL 컨테이너 타입들(비가상 소멸자를 가짐)을 상속을 피해라.

8.예외가 소멸자를 떠나지 못하도록 하자.

소멸자중 예외 발생시 프로그램을 바로 끝내거나 예외를 삼켜(무시) 버린다.

9.객체 생성 및 소멸 과정 중에는 절대로 가상함수를 호출하지 말자.

기본 클래스의 생성자가 호출될 동안 가상 함수는 절대로 파생 클래스 쪽으로 내려가지 않는다. 기본 클래스 생성자는 파생 클래스 생성자보다 앞서서 실행되기 때문에 기본 클래스 생성자가 돌아가고 있을 시점에 파생 클래스 데이터 멤버는 아직 초기화된 상태가 아니라는 것을 명심.

10.대입 연산자(Widget& operator=(const Widget& rhs)) 는 *this의 참조자를 반환하게 하자.

( =, +=, -=, *= 모두 포함)

11.operator= 에는 자기대입(자기자신을 대입하는경우)에 대한 처리가 빠지지 않도록 한다.

객체 같은지(this == &rhs) 들을 비교하여 같은경우 처리하지 않을수 있으나 일치성 검사 코드가 들어가면 소스코드가 커지고 실행 시간 속력이 줄어든다. 복사 후 맞바꾸기(Copy and Swap)을 사용하여 처리하자.

12.객체의 모든 부분을 빠짐 없이 복사하자.

해당 클래스의 데이터 멤버를 모두 복사하고, 이 클래스가 상속한 기본 클래스의 복사 함수도 꼬박꼬박 호출해 주자.

13.자원 관리는 객체가 하도록 한다. (RAII)

14.자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자.

15.자원 관리 클래스에서 관리되는 자원은 외부에서 접근 할 수 있도록 하자.

(명시적 변환 - get())

16.new 및 delete를 사용할 때는 형태를 반드시 맞추자.

17.new 로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자.

processWidget( std::tr1::shared_ptr( new Widget ) , priority() ) ; // 컴파일러에 따라 어떤 순으로 호출될지 알 수 없음.
std::tr1::shared_ptr pw( new Widget ) ;
processWidget( pw , priority() ) ;

18,인터페이스 설게는 제대로 쓰기엔 쉽게 엉터리로 쓰기엔 어렵게 하자.

19.클래스 설계는 타입 설계와 똑같이 취급하자.

20.’값에 의한 전달’보다는 ‘상수 객체 참조자에 의한 전달(const Widget& w)’ 방식을 사용하는 것이 효율(사본을 말들지 않음)및 안정성(복사 손실-slicing problem)에 더 좋다.

STL 반복자와 함수객체, 기본제공 타입(int, double…)은 ‘값에 의한 전달’이 더 적절하다.

21.함수에서 객체를 반환해야 할 경우에 참조자를 반화하려고 들지 말자.

대신 반환값 최적화 (return value optimization : RVO )를 이용할것.

21.데이터 멤버가 선언될 곳은 private 영역이다.

22.멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자.

캡슐화 정도가 높아지고 패키징 유연성도 커지고 기능적인 확장성도 늘어난다.

23.타입변환이 모든 매개변수에 대해 적용되어야 한다면 비 멤버 함수를 선언하자.

const Rational operator*(const Rational& lhs, const Rational& rhs)

24.예외를 던지지 않는 swap에 대한 지원도 생각해보자.

25.변수 정의는 늦출 수 있는데 까지 늦추자.

26.캐스팅은 웬만하면 사용하지 말자.

만약 필요하다면 C++ 캐스팅 (const_cast, static_cast, dynamic_cast, reinterpret_cast)를 사용할것.

27.내부에서 객체에 대한 핸들(참조자, 포인터, 반복자) 을 반환하는 코드느 되도록 피하자.

28.예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!

( 기본적인 보장, 강력한 보장, 예외 불가 보장 )

29.인라인 함수는 잘 따져서 사용해라.

함수 내부에 루프가 들어있거나(재귀) 가상 함수 호출, 인라인 함수의 주소를 취하는 코드(인라인된 함수로 선언된 함수를 함수 포인터를 통해 호출하는경우)는 인라인화 되지 않는다.

30.파일 사이의 컴파이 의존성을 최대로 줄이자.

전방선언, 브리지 패턴(핸들 클래스), 인터페이스 클래스등을 사용한다.

31.public 상속은 반드시 “is-a” 를 따르자.

32.상속된 이름을 숨기는 일은 피하자.

어쩔수 없는경우 using base::funcName 을 선언하여 끄집어 내자.

33.인터페이스 상속과 구현상속의 차이를 제대로 파악하고 구별하자.

34.가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때대로 길러두자

( 즉, 디자인패턴및 OOP관련 서적을 읽어보라는 것이다. )

35.상속받은 비가상 함수를 파생 클래스에서 재정의하지 말아라.

36.어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자.

기본매개 변수 값은 동적바인딩이 아닌 정적 바인딩으로 결정(resolved) 된다.

37.”has-a” 혹은 “is-implemented-in-terms-of”를 모형화 할 때는 객체 합성을 사용하자.

(상속보다 포함,합성을 이용 할 것 )

38.private 상속은 심사숙고해서 구사하자.

private상속은 구현 상속이다. 구현시 할 수 있으면 객체 합성을 사용하고 이외 비공개 멤버를 접근할 때 혹은 가상함수를 재정의할 경우 private 상속을 사용해라.

39.다중 상속(MI)은 심사숙고해서 사용하자.

다중상속 설계와 동등한 효과를 내는 단일 상속 설계를 뽑을 수 있다면 단일 상속 쪽으로 가는것이 확실히 좋다. 다중상속시 심심치 않게 나오는 경우가 “죽음의 마름모꼴 다중 상속” 인데 이 상속의 단점을 없애기위해 가상상속을 사용 할 수는있느나 가상 상속은 비싸다는 것이다. 가상 상속을 적법하게 쓰는 경우는 인터페이스 클래스로부터 public 상속을 시킴과 동시에 구현을 돕는 클래스로부터 private 상속을 시키면 된다.

40.암시적 인터페이스와 컴파일 타임 다형성을 알자.

41.typename 의 두가지 의미를 제대로 파악하자.

 template<class T > class Widget ;
 template<typename T> class Widget ;

class 와 typename 은 템플릿의 타입 매개변수를 선언할 때는 완전히 똑같다. 하지만 템플릿 매개변수에 종속된 것을 가리켜 의존 이름 (dependent name)이라고 하는데 이런 의존 이름이 어떤 클래스 안에 중첩되어 있는 경우 앞에 반드시 typename 을 써서 그것이 타입이라고 알려 주어야 한다.

template< class T>
void print2dn(const T& container) {
   if(container.size() >= 2 ) // 암시적 인터페이스 size() {
   typename T::const_iterator itr(container.begin()) ;
   ...
   }
}

typename은 중첩 의존 이름만 식별하는데 써야한다. 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있을 경우에는 typename을 쓰면 안된다.

42.템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자

a.this-> 를 사용한다. b.using 선언을 사용한다. c.호출할 함수가 기본 클래스의 함수라는 점을 명시적으로 지정한다. (하지만 호출되는 함수가 가상함수인 경우에는 이런식의 명시적 한정은 가상 함수 바인딩이 무시되어버린다.)

43.매개 변수에 독립적인 코드는 코드 비대화를 막기 위해 템플릿으로부터 분리시키자.

44.호환되는 모든 타입을 받아들이는 데는 멤버 함수 템플릿이 직방

45.타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자.

46.타입에 대한 정보가 필요하다면 특성정보 클래스를 사용하자.

47.템플릿 메타프로그래밍, 하지 않겠는가?

49.new 처리자의 동작 원리를 제대로 이해하자.

50.new 및 delete를 언제 바꿔야 좋은 소리를 들을지를 파악해 두자.

51.new 및 delete를 작성할 때 따라야 할 기존의 관례를 잘 알아 두자.

52.위치지정 new를 작성한다면 위치지정 delete도 같이 준비하자.

53.컴파일러 경고를 최 상위 레벨로 하고 한개라도 무시하지 말고 코딩하자.

54.TR1을 포함한 표준 라이브러 구성요소와 편안한 친구가 되자.

TR1은 현재 VC2008 feature pack 에 구현되어 있다. VC2008 을 사용하지 않는 경우 boost library 를 사용하면 된다.

55.부스트 라이브러리를 늘 여러분 가까이에.

Tweet
comments powered by Disqus