5월, 2016의 게시물 표시

[Effective C++ 3판] Chapter 7. 템플릿과 일반화 프로그래밍 (항목 41~ 48)

Chapter 7. 템플릿과 일반화 프로그래밍 (항목 41~ 48) 항목 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터. [이것만은 잊지 말자] - 클래스 및 템플릿은 모두 인터페이스와 다형성을 지원합니다. - 클래스의 경우, 인터페이스는 명시적이며 함수의 시그너처를 중심으로 구성되어 있습니다. 다형성은 프로그램 실행 중에 가상 함수를 통해 나타납니다. - 템플릿 매개변수의 경우, 인터페이스는 암시적이며 유효 표현식에 기반을 두어 구성됩니다. 다형성은 컴파일 중에 템플릿 인스턴스화와 함수 오버로딩 모호성 해결을 통해 나타납니다. 항목 42. typename의 두 가지 의미를 제대로 파악하자. // 중첩 의존 이름은 기본적으로 타입이 아니라고 가정된다. // 중첩 의존 이름이 타입임을 알려주기 위해서는 typename 키워드를 사용해야한다. template <class T> void example(void) { int a; // 비의존 이름(non-dependent name) T b; // 의존 이름(dependent name) auto c = T::variable; // 중첩 의존 이름(nested dependent name) typename T::type d; // 중첩 의존 타입 이름(nested dependent type name) } // typename은 중첩 의존 이름을 식별하는 데에만 사용할 수 있다. template <class T> void func(const T& obj); // typename 쓰면 안 됨. // 중첩 의존 이름이 '문맥상' 타입일 수 밖에 없는 경우에는 typename을 쓰면 안된다. template <class T> class Derived :public Base<T>::musttype { Derived(int val) : Base<T>

[Effective C++ 3판] Chapter 6. 상속, 그리고 객체 지향 설계 (항목 32~40)

이미지
Chapter 6. 상속, 그리고 객체 지향 설계 (항목 32~40) 항목 32. public 상속 모형은 반드시 "is-a(...는 ...의 일종이다)"를 따르도록 만들자. 상속 관계를 설계할 때 자연어 혹은 직관을 따르는 경우가 많다. 하지만 가끔 그것이 엄밀하지 못해서 문제가 생길 때가 있다. 따라서 public 상속을 할 때 is-a관계가 성립하는 지 철저히 따져봐야한다. 그리고 개인적으로 클래스 설계에 있어서 직관과 논리가 일치할 수 있도록 설계하는 게 정말 중요하다고 생각한다. [이것만은 잊지 말자] - public 상속의 의미는 is-a입니다. 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 합니다. 왜냐하면 모든 파생 클래스 객체는 기본 클래스 객체의 일종이기 때문입니다. 항목 33. 상속된 이름을 숨기는 일은 피하자. // using 선언을 이용한 방법. // public 상속시, 가려진 이름을 볼 수 있게 하기 위해 사용. namespace using_declaration { class Base { public: void f() {} void f(int x) {} }; class Derived : public Base { public: using Base::f; // this statement must be public. void f(int x, int y) {} }; } // forwarding function을 이용한 방법. // private 상속시, 가려진 이름을 가진 것들중 일부분만을 볼 수 있게 하기 위해 사용. namespace forwarding_function { class Base { public: void f() {} void f(int x) {} }; class Derived : private Base {

[Effective C++ 3판] Chapter 5. 구현 (항목 26~31)

Chapter 5. 구현 (항목 26~31) 항목 26. 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자. 변수를 함수 처음에 다 정의할 때가 있다. 하지만 이러면 함수 내부에서 분기가 됨에 따라 특정 변수를 사용하지 않을 수 도 있기때문에 비효율적이다. 그래서 변수 정의도 효율을 고려하여 최적의 위치에 해야한다. // 방법 1 // 생성자 1번 + 소멸자 1번 + 대입 n번 for (Data data, int i = 0; i < n; ++i) { data = func(i); } // 방법 2 // 생성자 n번 + 소멸자 n번 for (int i = 0; i < n; ++i) { Data data = func(i); } // 방법 1 if cost(대입) > cost(생성자 + 소멸자), // 방법 2 otherwise. [이것만은 잊지 말자] - 변수 정의는 늦출 수 있을 때까지 늦춥시다. 프로그램이 더 깔끔해지며 효율도 좋아집니다. 항목 27. 캐스팅은 절약, 또 절약! 잊지 말자. [C style cast] (T) expression  <=>  T(expression) [C++ style cast] const_cast, dynamic_cast, reinterpret_cast, static_cast 캐스팅은 공짜가 아니다. (런타임시 실행되는 코드가 증가한다.) 따라서 절약할 수 있는 한 절약해야한다. 특히 dynamic_cast는 주의해야한다. [이것만은 잊지 말자] - 다른 방법이 가능하다면 캐스팅은 피하십시오. 특히 수행 성능에 민감한 코드에서 dynamic_cast는 몇 번이고 다시 생각하십시오. 설계 중에 캐스팅이 필요해졌다면, 캐스팅을 쓰지 않는 다른 방법을 시도해 보십시오. - 캐스팅이 어쩔 수 없이 필요하다면, 함수 안에 숨길 수 있도록 해 보십시오. 이렇게 하면 최소한 사용자는 자신의 코드에 캐스팅을 넣지 않고 이 함수를 호출할 수 있게 됩니다. -

[Effective C++ 3판] Chapter 4. 설계 및 선언 (항목 18~25)

Chapter 4. 설계 및 선언 (항목 18~25) 하하하 하루에 2개씩 포스팅한다고 해놓고 한 개도 안했었네요~ ㅎㅎㅎ 하하하ㅏㅎㅎㅎ하하 항목 18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자. class BadDate { public: BadDate(int month, int day, int year); }; class Month { public: static Month Jan; // Incorrect! static const Month& Feb() { // Case 1 static Month m(2); return m; } static Month Mar() { return Month(3); } // Case 2 private: explicit Month(int m) : month(m) {} int month; }; Month Month::Jan(1); struct Day { explicit Day(int d) : day(d) {} int day; }; struct Year { explicit Year(int y) : year(y) {} int year; }; class Date { public: Date(Month month, Day day, Year year); }; int main() { BadDate badDate(1996, 2, 2); // mistake of user! Date date(Month::Feb(), Day(2), Year(1996)); }   위 코드를 봐보자. BadDate의 경우 날짜를 그냥 int로만 받으면 사용자의 실수로 인해 parameter의 순서가 바뀌는 일이 생길 수 있다. 따라서 사용자가 명시적으로 타입을 지정하도록 강제하면 사용자에게 실수를 컴파일타임에 알려줄 수 있

[Effective C++ 3판] Chapter 3. 자원 관리 (항목 13~17)

Chapter 3. 자원 관리 정보처리산업기사 시험 & 시골에 제사지내러 내려가느라 포스팅이 늦었습니다 ㅠㅜ 이번 Chapter는 자원 관리입니다. C++의 자원 관리 철학은 RAII이죠~ 객체가 생성되고 소멸될 때 생성자와 소멸자가 호출되는 원리를 이용하여 자원과 객체의 life cycle을 동일시함으로서 더욱 더 편리하고 안정적으로 자원 관리를 할 수 있습니다! 항목 13 : 자원 관리에는 객체가 그만! std::shared_ptr의 소멸자는 기본적으로 내부적으로 delete를 사용합니다. (delete[]가 아니라) 그래서 동적할당된 배열에 대해 std::shared_ptr를 사용하면 안됩니다. 대신에 std::vector나 C++11의 std::array 를 사용함으로서 raw array를 대신하는 방법이 있습니다. 또는 std::shared_ptr의 생성자에 custom deleter를 등록하는 방법이 있습니다. http://stackoverflow.com/questions/13061979/shared-ptr-to-an-array-should-it-be-used [ 이것만은 잊지 말자! ] - 자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하는 RAII객체를 사용합시다. - 일반적으로 널리 쓰이는 RAII 클래스는 tr1::shared_ptr (C++11부터 표준이 되어 std::shared_ptr로 쓰면 된다.) 그리고 auto_ptr (C++11부터 deprecated되었다. 대신에 대체재로서 std::unique_ptr를 사용하면 된다.) 입니다. 항목 14 : 자원 관리 클래스의 복사 동작에 대해 진지하게 고찰하자. RAII객체가 복사될 때 어떤 동작이 이루어져야 할까요? - 복사를 금지합니다. (Uncopyable 클래스 상속, =delete 활용) - 관리하고 있는 자원에 대해 참조 카운팅을 수행합니다. (custom deleter와 함께 std::shared_ptr를 활

[Effective C++ 3판] Chapter 2. 생성자, 소멸자 및 대입 연산자 (항목 5~12)

Chapter 2 . 생성자, 소멸자 및 대입 연산자 항목 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자. 항목 6 : 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자. 이러한 것과 관련된 규칙으로서 rule of three/five/zero 라는 것이 있다. 아래 링크 참조. http://en.cppreference.com/w/cpp/language/rule_of_three https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming) [ 정리해 두고 싶은 것 & 추가적인 사항 ] // C++11에서는 =delete를 사용하면 되긴 하지만, 책을 읽다가 인상깊어서 남겨본다. // 부스트에도 비슷하게 noncopyable이라는게 있다고 한다. class UnCopyable { protected: UnCopyable() {} ~UnCopyable() {} private: // 선언만 함으로서 friend에 대해서도 안전성 확보. UnCopyable(const UnCopyable&); UnCopyable& operator=(const UnCopyable&); }; class Example : private UnCopyable { }; [ 이것만은 잊지 말자! ] - 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 만들어 놓을 수 있습니다. (C++11에서는 이동 생성자, 이동 대입 연산자가 암시적으로 생성될 수 있다. - 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 두십시오. Upcopyable과 비슷한 기본 클래스를 쓰는 것도 한 방법입니다. (C++11에서는 =delete 를 활용하자.) 항목 7

[Effective C++ 3판] Chapter 1. C++에 왔으면 C++의 법을 따릅시다. (항목 1~4)

이미지
앞으로 Chapter별로 포스팅을 할 계획이다. 책의 내용을 완벽하게 정리하려는게 목적은 아니다. 내가 잘 몰랐던 것과 헷갈리는 것을 위주로 해서 개인적인 생각과 궁금한 것에 대한 추가적인 실험/연구 결과를 정리해나갈 것이다. Chapter 1. C++에 왔으면 C++의 법을 따릅시다. 항목 1 : C++를 언어들의 연합체로 바라보는 안목은 필수. [ 이것만은 잊지 말자! ] - C++을 사용한 효과적인 프로그래밍 규칙은 경우에 따라 달라집니다. 그 경우란, 바로 C++의 어떤 부분을 사용하느냐입니다. 항목 2 : #define을 쓰려거든 const, enum, inline을 떠올리자. #define에게는 유효범위라는 게 없다. 이 점 때문에 외부 라이브러리와 #define이 겹쳐서 고생한 기억이 여러 번 있다. (부들부들..) 그리고 #define은 그냥 단순 치환방식이기 때문에 매우 불안하다. 따라서 상수를 정의할 때는 const 변수를 사용하는 것이 좋다. 혹은 정수 타입에 해당하는 상수를 위해서 나열자 둔갑술 (enum hack)을 사용하는 방법도 있다. 나열자 둔갑술은 단순히 enum { ... }; 이런식으로 선언함으로서 해당 namespace에 한해서 정수형 상수를 정의할 수 있는 방법이다. [ 정리해 두고 싶은 것 & 추가적인 사항 ] - 정적 멤버로 만들어지는 정수류 타입의 클래스 내부 상수는 '정의'가 없어도 된다. [ 이것만은 잊지 말자! ] - 단순한 상수를 쓸 때는, #define보다 const 객체 혹은 enum을 우선 생각합시다. - 함수처럼 쓰이는 매크로를 만들려면 #define 매크로보다 인라인 함수를 우선 생각합시다. 항목 3 : 낌새만 보이면 const를 들이대 보자! [ 정리해 두고 싶은 것 & 추가적인 사항 ] // 컴파일러는 비트수준 상수성만 보장해준다. // 우리는 논리적 상수성을 고려해서 프로그래밍해야한다. cla