안전하고, 좋은 냄새가 나는 C++ 코드 만들기
C++ 을 배운지 꽤 오래 되었지만, 아직도 코드 리뷰를
통해 다른 개발자들의 코드를 볼 때면 한숨이 나오는 경우가 많습니다.
이런 경우가 반복되다 보니,
예전에 몇 가지 코딩 규칙 비슷하게 만들어놓은 자료가 있어 포스팅해 봅니다. 자료가 만들다 말아 내용이 중구난방이네요.. 앞으로도 계속 내용 보강해
나가겠습니다.
▤ 포인트 핸들
및 윈도우 핸들 체크
- 함수로 전달된 포인터 인자나, new
로 생성한 포인터 등 모든 포인터는 사용하기 직전 포인터 검사를 수행
- 가급적 STL 컨테이너를
사용해서 아예 포인터를 사용하지 않는 것이 정신건강에 이롭다. (STL 자체적으로 메모리 생성 및
소멸)
- 메모리 LEAK 을 막기
위해 SMART POINT 와 같은 Wrapper 클래스를
만든다.
- (윈도우 프로그램의 경우)윈도우 API 호출 직전 항상 윈도우 핸들이 정상인지 IsWindow 와
같은 함수로 검사한다.
▤ DRY(Don't Repeat Your Code)원칙
- 같은 코드를 여기 저기 중복해서 사용하지 말아야 한다. (☞ 실용주의 프로그래머 참고). 중복해서 사용할 경우 코드 량이
늘어날 뿐만 아니라 유지보수 cost 가 같이 늘어 나고,
추후 추가/개선/변경 등의 요구사항으로 인한
모듈 수정 시 여러 소스를 함께 수정해야 한다.
- 동일한 코드를 공용 루틴(함수)으로 개발 –
2~3줄 짜리 복잡한 if 문도 bool 를
리턴 하는 함수로 개발(☞ Refactoring
참고)
▤ 클래스간
종속성 최소화
- 대규모 프로젝트에서는 공통모듈이 변경되어도 이를 사용하는 모듈이
문제가 없도록 반드시 공통모듈은 인터페이스 기반(데이터가 없는,
virtual 가상함수로만 구성된)으로 구현한다.
(☞ The C++ Programming Language 참고)
- A 클래스 → B 클래스 → C 클래스를 사용할 경우 각 클래스는 자기가 직접 다루는 클래스(A
는 B, B 는 C) 만 알아야 한다. 즉 A 클래스는 C
클래스를 직/간접적으로 include 해서는
안되며 C 클래스의 변경으로 인해 A 클래스가 컴파일
되어서는 안 된다. A 클래스가 C 를 직접 접근할 경우가
있다면 B 클래스에 위임함수를 만들어 해결한다. (☞
실용주의 프로그래머 참고)
- B 클래스가 A 클래스로 어떤 데이터를 전달할 경우가
있다면(주로 이벤트 발생시 값을 전달하기 위해) A
클래스의 최소 인터페이스만을 B 클래스에 전달하여(COM
의 이벤트 소스 방식) 종속성을 최소화 한다.
(☞ Developer’s Workshop To COM and ATL 3.0 참고)
▤ 함수 인자
전달
- 함수의 인자는 최대 7개를
넘지 않아야 한다(☞ Code Complete 2
참고)
- 클래스 인스턴스를 함수 인자로 넘겨서는 안 된다(특히 함수 인자로 CString 을 넘기는 경우가 많음. CString → const CString& 나 CString*,
const char* 등으로 수정한다.
)
▤ 오류조건을
먼저 검사하여 오류 체크로 인해 코드 가독성이 떨어지지 않도록 하자.
- 특정 오류조건이 발생하면 더 이상 함수 수행에 의미 없을
경우, 이러한 오류 조건들은 함수초입단계부터 먼저 확인해서 바로 리턴 한다
- 위와 같이 오류조건이 먼저 확인되면 프로그램 코드에서 if depth 가 줄어 들고 코드 읽기가 수월해 지는 장점이 있다.
Before |
After |
void CClass::OnTest()
{ ... ... if
(m_pWndMain) {
DoSomething1(); } ... if
(m_pWndMain) {
DoSomething2(); } } |
void CClass::OnTest()
{ if (m_pWndMain ==
NULL) return; ... ... DoSomething1(); ... DoSomething2(); } |
▤ 긴 함수
쪼개기 (☞ Refactoring 참고)
- 긴 함수는 작은 여러 개의 함수로 쪼갠다(Refactoring 책에 의하면 2-3줄 짜리 함수로까지 쪼개어서
코드 가독성을 높이도록 하고 있음)
- 작은 함수로 쪼개다 보면,
작은 함수를 재활용하게 될 수 있고
- 긴 함수보다 코드 읽기가 수월해 지며
- 결국 유지보수가 쉬워진다
▤ enum 값 선언
시 _START, _END 를 활용하자(☞ CODE COMPLETE2 참고)
- enum 으로 선언된 값들을 변수나 for loop 를
이용하여 반복 계산할 경우가 종종 있는데 이 경우 enum 시작과,
끝을 나타내는 값을 enum 선언에 포함하면 유용하게 사용할 수 있다.
- 추후 enum 에 값을
추가해야 할 경우 _END 값을 사용한 배열은 코드 변경없이 크기가 늘어남
- For 루프 등에 _START, _END 을 사용할 수
있어 유지 보수에 유리
Before |
After |
enum WEEK_NAME {
SUNDAY =
0,
MONDAY = 1,
TUESDAY =
2,
WEDNESDAY =
3,
THURSDAY =
4,
FRIDAY =
5,
SATERDAY =
6, }; int nWeeks[SATERDAY + 1]; } |
enum WEEK_NAME { WEEK_STAR =
0,
SUNDAY =
0,
MONDAY = 1,
TUESDAY =
2,
WEDNESDAY =
3,
THURSDAY =
4,
FRIDAY =
5,
SATERDAY =
6, WEEK_END =
6, }; int nWeeks[WEEK_END + 1]; |
▤ 복잡한
제어문을 가진 루틴을 피하자
- If 문은 2 depth 이상 들어 갈 경우 refactoring 하라 (☞ CODE COMPLETE2)
① 대부분 코드를 제대로 이해하지 못해 복잡한 코드를 작성하고
있다.
② 개발자 90%는 2 depth 이상 들어가는 if 문장을 이해하지 못한다
- IF~ELSE IF ~ELSE IF ~ ….
ELSE 가 길게 이어질 경우
SWITCH 문으로 바꾼다
- IF 문이 길게 늘어 질 경우 함수로 분리한다
- C++ 의 factory 개념으로 상속을 이용한 클래스를
만드는 방법도 활용
'Tip & Tech' 카테고리의 다른 글
디버깅 메세지 출력 매크로 수정판 (0) | 2011.01.31 |
---|---|
GPG 명예의 전당.베스트 오브 베스트 (0) | 2011.01.21 |
콘솔,출력창을 이용한 실시간 디버깅 (0) | 2011.01.07 |
유용한 전처리기 (0) | 2011.01.07 |
게임브리오엔진에서 성능 향상을 위한 방법들.. (0) | 2010.12.20 |