C++ 창시자의 조언

인터넷에 떠돌아다니는 글을 가져온건데 실제 비야네 스트롭스트룹(Bjarne Stroustrup)이 쓴 글인지는 모르겠음.



C++ 창시자의 조언 1

1. 프로그래밍이란 어떤 문제에 대한 해결책 안에 여러분의 아이디어를 구체적으로 표현한 문장을 써 넣는 것이다. 프로그램의 구조는 이런 아이디어를 최대한 직접적으로 드러내도록 하자.
a. 별도의 아이디어로 생각되는 '것'은 클래스로 만든다.
b. 별도의 개체(entity)로 생각되는 '것'은 클래스의 객체로 만든다.
c. 공통의 인터페이스를 가지면, 이 인터페이스는 추상 클래스로 만든다.
d. 두 클래스의 구현 코드가 주요 부분에서 똑같으면, 공통된 부분을 뽑아 기본 클래스로 만든다.
e. 객체의 컨테이너가 될 클래스는 템플릿으로 만든다.
f. 컨테이너에 대한 알고리즘을 구현하는 함수는 동일 계열의 컨테이너군에 모두 적용할 수 있는 알고리즘을 구현하는 템플릿 함수로 만든다.
g. 어떤 클래스, 템플릿이 서로 논리적으로 가까운 관계를 가지고 있으면 이런 것들은 공통의 네임스페이스에 넣는다.

2. 배열이나 복소수처럼 수학적 의미를 가진 개체를 구현하지 않은 클래스를 정의하거나 연결 리스트 같은 하위 수준 타입을 정의하는 경우에는 다음을 따른다.
a. 전역 데이터를 사용하지 않는다(멤버를 사용한다).
b. 전역 함수를 사용하지 않는다. 
c. public 멤버 데이터를 사용하지 않는다. 
d. 프렌드를 사용하지 않는다. 단, a와 c를 피하기 위한 경우는 예외이다.
e. 클래스에는 '타입필드'를 넣지 않는다. 대신에 가상함수를 사용한다.
f. 인라인 함수를 사용하지 않는다. 단, 무시할 수 없는 최적화가 필요한 상황에서는 예외이다.



C++ 창시자의 조언 2

1. 어떤 문제에도 당황하지 말자! 시간이 약이다. 생각하고 생각하면 풀린다.

2. 훌륭한 프로그램을 만들기 위해 C++ 공부의 끝을 보려고 생각하지 않아도 된다.

3. 언어에서 어떤 기능이 제공되는지 신경 쓸 시간에 프로그래밍 기술에 집중하자.



C++ 창시자의 조언 3

1. 바퀴를 다시 만들려고 들지 말자. 라이브러리의 사용을 꺼림칙해 하지 말자.

2. 조화를 잊지 말것! 라이브러리의 용도, 동작원리, 동작에 필요한 비용을 정확히 파악하자.

3. 여럿 중 하나를 골라야 한다면 표준 라이브러리가 낫다.

4. 그렇다고 표준 라이브러리가 어디든 딱 맞을 거란 편견은 버릴 것.

5. 사용하려는 것에 맞는 헤더를 #include 하자. 

6. 표준 라이브러리에서 제공하는 모든 것은 std 네임 스페이스에 들어 있다.

7. char*를 쓰려거든 string을 사용하자.

8. 의심 많은 분은 범위 점검 벡터(Vec등)를 사용하자.

9. T[]를 쓰려거든 vector<T>, list<T> 및 map<key, value>를 사용하자.

10. 원소를 컨테이너에 추가할 땐 push_back()과 back_inserter()를 사용하자.

11. 배열에 realloc()을 쓰려거든, vector에 push_back()을 사용하자.

12. 공통적인 예외는 main()에서 받아 처리하자.



C++ 창시자의 조언 4

1. 유효 범위는 최대한 좁히자.

2. 한 범위 안에서 같은 이름을 쓰는 일은 절대로 하지 말 것. 이것은 어떤 범위를 둘러싼 바깥쪽 유효 범위에 대해서도 마찬가지다.

3. 선언문 하나에 대해 이름도 하나만 선언하자.

4. 관습적인 이름과 지역 이름은 짧게 만들고, 자주 사용되지 않는 이름 및 비지역 이름은 길게 지을 것.

5. 비슷한 이름들을 함께 쓰는 일은 피할 것.

6. 이름 짓기 방식은 일관성을 유지하자.

7. 이름을 선택할 때는, 구현에 신경 쓰지 말고 의미에 각별히 신경 쓸 것.

8. 어떤 기본제공 타입의 값 표현 방식이 달라질 수 있는 경우에는, typedef 타입을 써서 그 타입에 대해 의미 있는 이름을 정의 하자.

9. typedef 타입은 다른 타입의 동의어로서 사용하는 것이다. 반면, 아예 새로운 타입을 정의하려면 나열자나 클래스를 사용하자.

10. 모든 선언문엔 반드시 타입이 주어져야 한다는 사실을 잊지 말것.

11. 문자의 숫자값에 대한 불필요한 가정은 삼가할 것.

12. 정수의 크기에 대한 불필요한 가정은 삼가할 것.

13. 부동소수점 실수 타입의 표현 범위에 대한 불필요한 가정은 삼가할 것.

14. short int 혹은 long int 보다 그냥 int 가 낫다.

15. float 혹은 long double 보다는 그냥 double이 낫다.

16. signed char 혹은 unsigned char 보다는 그냥 char가 낫다.

17. 객체의 크기에 대한 불필요한 가정은 삼가할 것.

18. 부호를 사용하지 않은 숫자 타입의 산술 연산은 피할 것.

19. signed가 unsigned로 변환되는 경우와 unsigned가 signed로 변환되는 경우를 조심스럽게 살필 것.

20. (부동소수점)실수 타입을 정수로 변환하는 경우를 조심스럽게 살필 것.

21. 더 작은 타입으로 변환하는 경우, 이를테면 int를 char로 바꾸는 경우를 조심스럽게 살필 것.



C++ 창시자의 조언 5

1. 호용된 것 이외의 포인터 연산은 금물.

2. 배열의 경계를 넘어선 데이터 기록은 하지 않도록 각별한 주의를 기울일 것.

3. NULL보다 0을 사용할 것.

4. 기본제공(C 방식) 배열을 쓰려거든 vector와 valarray를 사용할 것.

5. 0으로 끝나는 문자 배열을 쓰려거든 string을 사용할 것.

6. 상수 제약이 없는 참조자 인자를 사용하는 일은 최대한 줄일 것.

7. 하부 수준의 코드 이외에는 void*를 피하자.

8. 코드 중 의미를 부여해야하는 곳에 리터럴(매직넘버)를 사용하지 말자. 그 대신에 기호형태의 상수를 정의하여 사용하는 것을 습관화 할것.



C++ 창시자의 조언 6

1. 비표준의 서드파티 라이브러리 및 직접 손으로 만든 코드 보다는 표준 라이브러리를 우선적으로 고려할 것.

2. 복잡한 표현식은 금물

3. 연산자 우선순위가 의심되면 괄호를 사용할 것.

4. 명시적 타입 변환(캐스트)을 피할 것.

5. 명시적 타입 변환을 굳이 사용해야한다면, C스타일의 캐스트 대신에 C++전용 캐스트 연산자를 사용할 것.

6. 결과값이 잘 정의된 경우에만 T(e) 표기법을 사용할 것.

7. 표현식 평가 순서가 불명확해지는 경우를 피하자.

8. goto는 가급적 기피대상.

9. do문도 마찬가지

10. 어떤 값으로 초기화되기 전엔 그 변수를 선언하지 말자.

11. 주석문은 언제나 또박또박, 명확하게.

12. 들여쓰기는 일관성 있게 구사하자.

13. 전역 함수 버전의 operator new()를 대신하고 싶으면 멤버 버전의 operator new()를 우선적으로 고려하자.

14. 입력을 처리할 때는 언제든 입력이 엉망으로 들어올 수 있음을 명심할 것.



C++ 창시자의 조언 7

1. 비상수 참조자 타입의 인자는 다시 보고 또 보자. 인자 자체의 내용을 함수에서 변경하고자 한다면, 차라리 포인터를 사용하고 값으로 반환하자.

2. 인자 복사에 걸리는 부담을 최소화할 목적이라면 상수 참조자 타입의 인자를 사용할 것.

3. const 키워드를 널리 그리고 꾸준히 사용하자.

4. 매크로도 가급적 기피 대상.

5. 인자의 개수가 정해지지 않는 함수는 피할 것.

6. 지역 변수에 대한 포인터나 참조자를 반환하지 않는다.

7. 다른 타입에 대한 개념적으로 동일한 동작을 수행하는 함수들이 있다면 오버로딩을 사용할 것.

8. 정수 타입 인자에 대해 오버로딩할 때는 일반적으로 발생할 수 있는 모호성을 없애는 함수를 만들어 줄 것.

9. 함수 포인터의 사용을 고려하려거든 가상함수 혹은 템플릿 중에서 적절히 골라잡자.

10. 매크로를 필히 써야한다면, 대문자를 많이 사용하여 눈에 잘 띄는 이름을 붙이자.



C++ 창시자의 조언 8

1. 프로그램의 논리적 구조를 표현하는 데는 네임스페이스가 그만이다.

2. 전역 이름은 main()만 남기고 모두 네임스페이스에 몰아넣을 것.

3. 네임스페이스를 설계할 때는 관련도 없는 네임스페이스를 아무 생각 없이 접근하게 만들지 말고 간편하게 사용할 수 있도록 설계할 것.

4. 네임스페이스에 너무 짧은 이름을 붙이지 않도록 하자.

5. 필요하면 긴 네임스페이스 이름 대신에 쓸 별칭을 사용하자.

6. 여러분의 네임스페이스를 쓰는 다른 사용자들에게 네임스페이스 표기에 대한 부담을 너무 많이 지우지 말 것.

7. 이미 선언된 네임스페이스 멤버를 정의할 때는 Namespace::member 표기를 사용할 것.

8. using 지시자를 쓰는 경우는 프로그램 재설계 도중 혹은 지역 유효범위 내부로만 한정시킬 것.

9. 일반적인 동작을 담당한 코드와 에러 처리를 맡은 코드를 떼어 놓는 데는 예외가 좋다.

10. 예외로 쓰기 좋은 타입으로는 기본제공 타입보다 사용자 정의 타입을 적극 추천한다.

11. 내부 수습으로 충분하다면 굳이 예외를 사용하지 말 것.



C++ 창시자의 조언 9

1. 인터페이스와 논리적 구조를 나타내는 데는 헤더 파일을 사용할 것.

2. 어떤 헤더에 선언된 함수를 구현하는 소스 파일은 그 헤더를 #include 할 것.

3. 서로 다른 해석 단위에 있는 전역 개체(데이터, 함수, ...)를 정의 할 때는 똑같은 이름은 우선적으로 피하고 비슷한 이름에 다른 뜻을 가진 것들도 피할 것.

4. 인라인함수가 아닌 함수는 헤더안에 정의하지 말자.

5. #include가 사용될 곳은 오직 전역 유효범위와 네임스페이스 내부뿐이다.

6. #include할 헤더는 반드시 완전한 선언 및 정의를 담고 있도록 하자.

7. #include 방지자를 사용할 것.

8. C 헤더를 #include 할 때 전역 네임스페이스를 침범하는 것을 피하려면 네임스페이스를 사용하자.

9. 자체에 모든 것을 갖춘 헤더가 좋다.

10. 사용자용 인터페이스와 구현자용 인터페이스를 구분해서 만들어 두자.

11. 보통 사용자용 인터페이스와 전문 사용자용 인터페이스를 구분해서 제공하자.

12. 다른 언어로 작성된 프로그램에 섞어서 쓸 C++코드에는 런타임 초기화가 필요한 비지역 객체가 들어가지 않도록 신경 쓰자.



C++ 창시자의 조언 10

1. 개념을 클래스로 나타내자.

2. 멤버 전체가 public인 클래스는, 이 클래스가 진짜 단순한 데이터로만 쓰일 뿐이며 각 데이터 멤버에 대해 불변속성을 설정할 필요가 없을 경우에만 선언/사용할 것.

3. 구체 타입은 가장 간단한 형태의 클래스이다. 구체 타입을 쉽게 쓸 수 있다면, 복잡한 클래스와 평이한 자료구조를 사용하려거든 이것을 선호할 것. 휠씬 낫다.

4. 클래스의 내부 정보에 직접 접근해야하는 함수만 멤버로 만들 것.

5. 클래스와 그 클래스의 보조함수 사이의 명시적인 연결고리를 만드는 데는 네임스페이스가 최고.

6. 클래스 객체의 값을 바꾸지 않는 멤버 함수는 상수(const)멤버 함수로 만들자.

7. 클래스의 내부 정보에 접근해야 하지만 개개의 객체에 대해 호출할 필요가 없는 함수는 정적 멤버 함수로 만들 것.

8. 클래스의 불변속성 구축은 생성자에서 수행할 것.

9. 생성자에서 자원을 획득했다면, 이 생성자가 속한 클래스에는 자원을 해제하는 소멸자가 있어야 한다.

10. 클래스에 포인터 멤버가 있는 경우에는 반드시 복사 연산을 정의해 주어야 한다. 

11. 클래스의 참조자 멤버에 대해서는, 경우에 따라서 복사 연산자를 정의해 주어야 한다. 

12. 복사 연산 혹은 소멸자가 필요한 클래스에 대해서는, 상황에 따라 생성자, 소멸자, 복사 대입 연산자, 복사 생성자를 정의해 주어야 한다.

13. 복사 대입 연산자 안에는 반드시 자기 대입 점검 코드를 넣어두자. 

14. 복사 생성자를 작성할 때는 복사되어야 하는 멤버는 반드시 빠트리지 않도록 주의하자.

15. 클래스에 데이터 멤버를 새로 추가할 때는 그 멤버에 해당하는 클래스에 사용자 정의 생성자가 있어서 멤버 초기화에 영향을 주는지 점검하자.

16. 클래스 선언문 안에 정수 상수를 정의해야 할 경우 나열자를 사용하자.

17. 전역 및 네임스페이스 객체를 정의할 경우, 생성자 호출 순서에 따라 달라지는 사항이 있는지 점검하자.

18. 생성자 호출 순서의 의존성을 최소화하는 데는 최초사용 점검 스위치를 사용하자. 

19. 잊지 말자. 임시 객체는 자신이 생성된 전체 표현식이 끝날 때 소멸된다는 것을.



C++ 창시자의 조언 11

1. 연산자를 오버로딩할 때는 기존의 용법을 그대로 따라 하는 데 우선적인 초점을 두자.

2. 크기가 큰 피연산자에 대해서는 상수 참조자 타입의 인자를 사용할 것.

3. 연산 결과를 담는 객체가 클 경우에는 반환값 최적화를 고려할 것.

4. 기본 복사 연산자의 동작이 현재의 클래스에 맞으면 그대로 둘 것.

5. 기본 복사 연산자의 동작이 현재의 클래스에 맞지 않으면 오버로딩하든지 private영역에 가둘 것.

6. 내부 표현 데이터에 접근해야 하는 데는 비멤버 함수보다 멤버 함수가 낫다.

7. 내부 표현 데이터에 접근할 필요가 없는 연산을 구현하는 데는 멤버 함수보다 비멤버 함수가 낫다.

8. 클래스를 지원하는 보조 함수들은 네임스페이스를 써서 서로 관련지어 주자.

9. 대칭형 연산자에 대해서는 비멤버 함수를 사용하자.

10. 다차원 배열의 첨자 연산은 () 연산자로 구현하자.

11. '크기 표시' 인자 하나만을 받아들이는 생성자는 반드시 명시호출 생성자로 만들자.

12. 특별한 경우가 아닌 이상 여러분이 만든 문자열 클래스보다는 표준 string이 훨씬 낫다.

13. 암시적 타입변환이 끼어들 수 있는 부분에 각별히 촉각을 세우도록.

14. 좌변값을 좌변 피연산자로 사용하는 연산자를 만들 때는 멤버함수를 사용하자.



C++ 창시자의 조언 12

1. 타입필드는 반드시 피할 것.

2. 복사손실(slicing)을 피하려면 객체의 포인터 혹은 참조자를 사용해야 한다.

3. 깔끔한 인터페이스 제공에 목적을 두고 있다면 추상 클래스를 사용하자.

4. 인터페이스를 최소화하려면 추상 클래스를 사용하자.

5. 인터페이스로부터 구현 세부사항을 차단하려면 추상 클래스를 사용하자.

6. 사용자 코드를 건드리지 않은 채로 기존 함수의 구현코드를 새로 추가하려면 관련 함수들이 가상 함수로 되어 있어야 한다.

7. 사용자 코드의 재컴파일을 최소화하려면 추상 클래스를 사용하자.

8. 구현 세부사항이 여러개가 될 수 있는 경우를 허용하려면 역시 추상 클래스를 사용할 것.

9. 가상 함수를 가진 다형성 클래스에는 반드시 가상소멸자가 있어야 한다.

10. 추상 클래스에는 대개 생성자가 필요없다.

11. 겹치지 않는 개념들은 내부 표현 데이터도 반드시 분리해 놓도록 하자.



C++ 창시자의 조언 13

1. 많은 인자 타입에 적용할 수 있는 알고리즘을 작성하려면 템플릿을 사용할 것.

2. 컨테이너를 작성하려면 템플릿을 사용할 것.

3. 포인터를 담는 컨테이너를 구현할 때 코드 크기를 최소화하려면 특수화 버전을 제공할 것.

4. 템플릿의 특수화 버전 앞에 반드시 일반화 버전을 선언해 둘 것.

5. 템플릿 특수화 버전은 그것이 사용되는 지점 앞에 선언해 둘 것.

6. 템플릿이 인스턴스화되는 지점의 주변 환경에 대한 템플릿 정의의 의존도를 최소화하자.

7. 선언한 특수화 버전은 반드시 정의하자.

8. C스타일 문자열 및 배열에 대한 템플릿 특수화가 필요한지 생각할 것.

9. 무엇이든 매개변수화를 할 때는 정책 객체를 사용하자.

10. 타입은 다르지만 개념이 동일한 구현코드를 하나의 인터페이스를 통해 제공하려면 템플릿 특수화와 오버로딩을 사용하자.

11. 단순한 경우에 대해서는 단순한 인터페이스를 제공하고, 자주 등장하지 않는 경우에 대해서는 오버로딩 및 기본 인자를 사용하자.

12. 어떤 클래스를 템플릿으로 일반화하기 전에 반드시 구체적인 클래스를 써서 디버깅하자.

13. 어떤 파일에 들어 있는 템플릿 정의를 다른 해석 단위에서 끌어오려면, 해당 템플릿 정의를 export로 만들어 두어야 한다.

14. 크기가 큰 템플릿 및 외부 의존 정보가 적지 않은 템플릿은 분할 컴파일 하자.

15. 타입 변환에 템플릿을 쓰는 것은 좋은데 각별히 주의를 기울이자.

16. 필요하다면, constraint()멤버함수를 써서 템플릿 인자에 제약을 가할 수 있다. 

17. 컴파일 및 링크에 걸리는 시간을 최소화하려면 명시적 인스턴스화를 사용하자.

18. 런타임 효율이 가장 크게 문제되는 경우에는 파생 클래스보다 템플릿이 더 낫다.

19. 재컴파일 없이 새로운 타입을 추가해야할 경우에는 템플릿보다 파생 클래스가 더 낫다.

20. 공통 기본 클래스를 정의할 수 없는 경우에는 파생클래스 대신에 템플릿을 애용하자.

21. 기본제공 타입 및 구조체와의 호환성 제약이 중요한 경우에는 파생 클래스 대신에 템플릿을 애용하자.



C++ 창시자의 조언 14

1. 에러 처리에는 예외를 사용하자.

2. 지역적 흐름 제어 구조로도 충분한 곳에는 예외를 사용하지 말 것.

3. 자원 관리에는 "자원획득 즉 초기화"(Resource Acquisition is Initialization, RAII) 기법을 사용하자.

4. 모든 프로그램에 예외 안전성을 부여할 필요는 없다.

5. 불변속성을 유지하는 데도 "자원획득 즉 초기화" 기법 및 예외 처리자를 사용하자.

6. try 블록의 사용은 최대한 자제하자, 직접 에러 처리자 코드를 두기 보다는 "자원획득 즉 초기화" 방법을 적극적으로 활용하자.

7. 모든 함수에서 모든 가능한 에러를 도맡아 처리할 필요는 없다.

8. 생성자가 수행되다 오동작이 나면, 예외를 던져서 이것을 알릴 것.

9. 대입 연산 중에 예외를 던지기 전에, 모든 연산자가 유효한 상태에 있도록 하자.

10. 소멸자에서는 예외 발생을 피하자.

11. 모든 예외를 받아 보고하는 코드는 main()에 두도록.

12. 에러 처리와 상관 없는 코드와 에러 처리 코드는 격리 시킬 것.

13. 생성자에서 예외를 던지려 할 때 미리 생성자에서 자원이 획득된 상태라면, 해당 자원을 모두 해제하자.

14. 자원 관리는 단계적으로 진행되도록 하자.

15. 주로 사용하게 될 인터페이스에 대해서는 예외 지정 기능을 사용하자.

16. new에 의해 할당되었는데 예외 발생으로 인해 해제되지 않은 메모리가 메모리 누수를 일으킨다는 사실을 잊지말자.

17. 함수에서 발생할 수 있는 예외는 언젠가 발생할 것이란 가정을 항상 빠뜨리지 말도록.

18. 무릇 예외라면 exception 클래스에서 파생되어야 한다는 편견을 버리자.

19. 라이브러리는 자신의 판단만으로 바로 프로그램을 끝내 버리게 만들면 안된다. 예외를 발생시켜 라이브러리 호출자에게 판단을 넘기자.

20. 라이브러리는 최종 사용자를 염두에 둔 진단 출력 메시지를 만들어서도 안된다. 예외를 발생시켜 호출자에게 판단을 넘기자.

21. 예외 처리 전략은 설계 단계에서 세우자.



C++ 창시자의 조언 15

1. 두개 이상의 특징이 합쳐진 것을 나타내려면 통상적인 다중상속을 이용할 것.

2. 인터페이스로부터 구현 세부사항을 분리하여 사용하는 데는 다중상속을 쓰자.

3. 어떤 클래스들 사이에선 공통적이지만 그렇다고 클래스 계통 내의 전부에 대해서 그렇지는 않은 것을 나타내는 데는 가상 상속을 쓰자.

4. 명시적 타입 변환(캐스팅)은 되도록 피할 것.

5. 클래스 계통 내의 횡단이 필요한 경우에는 dynamic_cast를 사용하자.

6. typeid를 쓸 바엔 차라리 dynamic_cast가 더 낫다.

7. protected보다는 private가 여러모로 확실 하다.

8. 데이터 멤버는 protected로 선언해 놓지 말 것.

9. operator delete()를 따로 가지고 있는 클래스에는 가상 소멸자가 필요하다.

10. 생성자나 소멸자가 호출되고 있는 도중에는 가상 함수 호출은 절대 금물.

11. 멤버 이름을 컴파일러에게 확실히 알려주기 위한 명시적인 한정자 사용은 절약이 미덕이다. 함수 오버라이딩 할 때는 기회 있을 때마다 자주 사용할 것.

댓글

이 블로그의 인기 게시물

[NSIS] 32비트와 64비트 모듈 등록하는 법. (regsvr32)

[Visual Studio] Windows 7 에서 Visual Studio 6.0 디버그 시 프로세스 좀비되는 증상 해결 방법