본문 바로가기

Effective C++

(43)
항목 7: 다형성을 가진 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 시간 기록을 유지하는 방법은 활용에 따라 무궁무진 하다. 그래서 TimeKeeper 라는 기본 클래스를 만들어 놓은 후 적절한 용도에 따라 이것을 상속하도록 설계하면 딱 알맞을 거 같다. TimeKeeper를 쓰는 사용자들은 시간 정보에 접근하고 싶은데, TimeKeeper에서 시간 계산을 어떻게 하는지는 관심이 없고, 그냥 시간 정보에 접근만 하고 싶다. => 이럴 때 어떤 시간기록 객체에 대한 포인터를 반환하는 용도로 팩토리 함수(factory function, 새로 생성된 derived class 객체에 대한 base class 포인터를 반환하는 함수) 를 만들어 두면 딱 좋을 거 같다.(getTimeKeeper()) [C++] 팩토리 함수 :: IT's me (tistory.com) 참고 팩토리 ..
항목 6: 컴파일러가 만들어낸 함수가 필요없으면 확실히 이들의 사용을 금해 버리자. 부동산 거래 프로그램을 만든다. 그래서 class HomeforSale을 만드는데, 부동산 중개 업자가 꼭 신신당부한 말이 있다. 모든 자산은 세상에 하나밖에 없다!! 그럼 HomeForSale은 사본자체를 만들 수 없게 해야 한다. 그러다 보니 HomeForSale 객체를 복사하려 하면 컴파일 에러가 뜨게 하고 싶다. 일반적으로 어떤 클래스에서 특정한 종류의 기능을 지원하지 않았으면 하는 의도를 반영할려면, 그런 기능을 제공하는 함수를 선언하지 않으면 되는데, 복사 생성자와 복사 대입 연산자의 경우는 선언하지 않으면 컴파일러가 자동으로 선언해버리는게 문제다. 해결의 열쇠 : 기본 복사 생성자, 기본 복사 대입 연산자는 public member로 선언된다!! => private으로 바꿔버리면 됨 => 외..
항목 5: C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자. 객체지향 C++에서 클래스 안에 직접 선언해 넣지 않으면 컴파일러가 저절로 선언해 주도록 하는 맴버 함수들 이 있다. copy constructor copy assignment opeartor destructor default constructor 이때 이 맴버함수들은 모두 기본형, public, inline 함수이다. 컴파일러가 만들어주는 이 함수들은 무슨역할을 할까? => 컴파일러에게 "배후의 코드"를 깔 수 있는 자리를 마련하는 것이다. 배후의 코드 = base class 및 non-static member의 생성자, 소멸자를 호출하는 코드 이 때 소멸자는 derived class 가 상속한 base class의 소멸자가 virtual로 되있지 않으면 역시 non-virtual 소멸자로 만들어진다는..
항목 4: 객체를 사용하기 전에 반드시 그 객체를 초기화 하자 객체의 초기화에 있어서는 C++이 이랬다저랬다 하는 경향이 있다.(맘에 들지 않음) int x; 이 코드가 어떤 상황에서는 0으로 자동 초기화가 되지만, 또 다른 상황에서는 그렇지 않다는 것이다. class Point{ int x, y; }; Point p; 이렇게 했을 때에도, p의 데이터 맴버(x,y) 역시 어떤 상황에서는 0으로 자동 초기화가 되지만, 또 다른 상황에서는 안된다. => ???????????? 초기화되지 않은 값을 만약 그대로 읽으면 C++에서는 그대로 흘러 나오게 된다. 다른 언어에서는 뭐 초기화되지 않은 객체를 읽기만 해도 프로그램이 멈추는 경우도 있지만… C++의 객체 초기화가 중구난방인건 절대 아니다. 언제 초기화가 보장되며, 언제 그렇지 않은지에 대한 규칙이 명확하게 있지만,..
항목 3: 낌새만 보이면 const를 들이대 보자! cosnt = 외부 변경을 불가능 하게 하는 keyword => 값이 변하면 안되는게 맞다면, 우리도 반드시 지켜야 한다. 이렇게 해야 컴파일러가 const를 지키는데 한 몫 거들 수 있기 때문이다. char text[] = "hello"; char *p = text; 비상수 포인터, 비상수 데이터 const char *p = text; 비상수 포인터, 상수 데이터 char * const p = text; 상수 포인터, 비상수 데이터 const char * const p = text; 상수 포인터, 상수 데이터 iterator =: pointer std::vector vec; int arr[] = {1,2,3}; const std::vector::iterator iter = vec.begin(); con..
항목 2: #define을 쓰려거든 cosnt, enum, inline을 떠올리자 위 항목은 가급적 선행 처리자(#, preprosessor) 보다 컴파일러를 더 가까이 하자 라는 말임 #define RATIO 3.141592 라는 코드를 짰다고 생각하면, 컴파일러에게 넘어가기 전에 preprosessor가 RATIO라는 글자를 다 밀어버리고 3.14라는 상수로 바꾼다. 즉 RATIO라는 이름은 컴파일러가 쓰는 기호 테이블에 들어가지 않게 된다. 그래서 이 때 컴파일 에러라도 발생하게 되면 꽤나 헷갈릴 수 있다. RATIO에 에러가 났다고 해도 컴파일러는 3.14라는 상수에서 에러가 난 것으로 알기 때문에 에러 메세지에 RATIO라는 글자가 안 뜰 것이고 그럼 어디서 틀렸는지 햇갈릴 수 있다. 이 문제는 symbolic debugger(기호식 디버거)에서 나타날 수도 있다. 마찬가지로..
항목 1: C++를 언어들의 연합체로 바라보는 안목은 필수 초창기 C++ : C with Classes 라고 불릴 정도 예외(함수 구성방식 변화), 템플릿(새로운 프로그램 설계 방식), STL(확장성) 등등 => C++ : multiparadigm programming language (다중 패러다임 프로그래밍언어) 절차적 프로그래밍(구조적 프로그래밍) + 객체 지향 + 함수식(functional) + 일반화(generic) + metaprogramming 등등 여러 가지 패러다임이 포함되어있음. => 여러가지가 있는건 좋지만, 이들 사이에서 혼동을 느낄 수 있다. => C++ 를 단일 언어로 바라보는 시각에서 벗어나 상관관계가 있는 여러 언어들의 연합체로 봐야 한다. 그 후 각개격파 => C++는 여러 개의 하위언어를 제공한다고 봐야됨 C C++는 C를 기본으..