Books/Effective C++
[Effective C++] 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타입 다형성부터
[Effective C++] 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타입 다형성부터
2022.07.08클래스의 특성 객체 지향 프로그래밍을 이루는 축은 명시적 인터페이스와 런타임 다형성이다. class Widget { public: Widget(); virtual ~Widget(); virtual size_t size() const; virtual void normalize(); void swap(Widget& other); }; void Func(Widget& w) { if(w.size() > 10) { Widget temp(w); temp.normalize(); temp.swap(w); } } 명시적 인터페이스 Func 함수의 매개변수인 w는 Widget 클래스의 객체이므로 w는 Widget 인터페이스를 지원해야 한다. 이 인터페이스를 소스코드(Widget 클래스가 있는 헤더파일 등)에서 찾으면 확인..
[Effective C++] 40. 다중 상속은 심사숙고해서 사용하자
[Effective C++] 40. 다중 상속은 심사숙고해서 사용하자
2022.07.03다중 상속의 의미 둘 이상의 클래스로부터 상속을 받는 것 다중 상속의 문제점 함수 호출의 모호성 둘 이상의 기본 클래스로부터 똑같은 이름(함수, typedef 등)을 물려받을 가능성이 생긴다는 점이다. 즉, 모호성이 생긴다는 것인데 아래의 예제를 봐보자. class BorrowableItem { public: void checkOut(); ... }; class ElectronicGadget { private: bool checkOut() const; ... }; class MP3Player:public BorrowableItem, public ElectronicGadget {...}; MP3Player mp; mp.checkOut(); // 모호성 발생! 이것은 중복된 함수 호출 중 하나를 골라내는 C..
[Effective C++] 39. private 상속은 심사숙고해서 구사하자
[Effective C++] 39. private 상속은 심사숙고해서 구사하자
2022.07.03private 상속 public 상속은 is-a관계로 나타낸다. 이 관계는 Student가 Person으로부터 public 상속으로 파생된 상태의 클래스 계통이 주어지면 함수 호출을 성공시키기 위해 컴파일러가 Student를 Person으로 암시적 변환을 수행하는 예를 통해 잘 설명이 된다.(항목 32 참고) 그럼 private 상속은 뭘 의미하는 것일까? class Person {...}; class Student: private Person {...}; // private 상속 void eat(const Person& p); void study(const Student& s); Person p; Student s; eat(p); // OK eat(s); // Error, Student는 Person의..
[Effective C++] 38. "has-a(..는..를 가짐)" 혹은 "is-implemented-in-terms-of(..는..를 써서 구현됨)"를 모형화할 때는 객체 합성을 사용하자
[Effective C++] 38. "has-a(..는..를 가짐)" 혹은 "is-implemented-in-terms-of(..는..를 써서 구현됨)"를 모형화할 때는 객체 합성을 사용하자
2022.07.03합성 항목 32에서 public 상속의 의미는 "is-a(.. 는.. 의 일종이다)"라는 사실을 알았다. 객체의 합성 또한 두 가지 의미를 가지고 있는데, "has-a(.. 는.. 를 가짐)"과 "is-implemented-in-terms-of(.. 는.. 를 써서 구현됨)"을 뜻할 수도 있다. Has-a(.. 는.. 를 가짐) class Address {...}; class PhoneNumber {...}; class Person { public: ... private: std::string name; Address address; PhoneNumber voiceNumber; }; 위의 예시를 보면 Person은 name과 address, voiceNumber 등을 가지고 있다. 이렇게 소유의 개념이 ..
[Effective C++] 37. 어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자
[Effective C++] 37. 어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자
2022.07.02바인딩(binding) 프로그램 소스에 쓰인 각종 내부 요소, 이름, 식별자들에 대해 값 혹은 속성을 확정하는 과정을 일컫는다. 이 과정이 빌드 중에 이루어지면 정적 바인딩이라고 하고, 실행 중에 이루어지면 동적 바인딩이라고 한다. 공식적으로, 정적 바인딩은 선행 바인딩이란 다른 이름으로도 알려져 있고 동적 바인딩은 지연 바인딩이란 이름으로도 알려져 있다. 기본 매개변수 값을 가진 가상 함수를 상속하는 경우 가상 함수는 동적으로 바인딩되지만, 기본 매개변수 값은 정적으로 바인딩된다. 객체의 정적 타입(static type)은 프로그램 소스 안에 놓는 선언문을 통해 그 객체가 갖는 타입이다. 아래의 클래스 계통을 보자. class Shape { public: enum ShapeColor( Red, Gree..
[Effective C++] 36. 상속받은 비가상 함수를 파생 클래스에서 재정의하는 것은 절대 금물!
[Effective C++] 36. 상속받은 비가상 함수를 파생 클래스에서 재정의하는 것은 절대 금물!
2022.07.02결론부터 얘기하면, 상속받은 비가상 함수를 파생 클래스에서 재정의 하지 말자는 것이다. 상속받은 비가상 함수를 파생 클래스에서 재정의한 아래의 예시를 보자. class B { public: void mf(); ... }; class D: public B { public: void mf(); ... }; --- D x; B *pB = &x; pB->mf(); // B::mf()를 호출 D *pD = &x; pD->mf(); // D::mf()를 호출 이렇게 두 가지 동작을 하게 되는 이유는 B::mf 및 D::mf 등의 비가상 함수는 정적 바인딩(static binding)으로 묶이기 때문이다. 즉, pB는 'B에 대한 포인터' 타입으로 선언되었기 때문에 pB를 통해 호출되는 비가상 함수는 항상 B 클래스..
[Effective C++] 35. 가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자
[Effective C++] 35. 가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자
2022.06.26순수 가상 함수가 아닌 가상 함수로 선언되어 있다는 것은 기본 구현이 제공된다는 사실을 34장을 통해 알 수 있었다. 하지만, 정작 다른 작동을 해야 할 때 까먹고 재정의를 하지 않으면 디버깅이 아주 힘들어질 것이다. 이번 장은 이러한 문제를 해결하기 위해 가상 함수를 대체할 무언가가 필요하다는 내용을 담고 있다. 비가상 인터페이스(Non-Virtual Interface: NVI) 관용구를 통한 템플릿 메서드 패턴 가상 함수는 반드시 private 멤버로 두어야 한다고 주장하는 사람들이 제안하는 설계이다.(가상 함수 은폐론) 이 이론에 따르면, healthValue는 public 비가상 함수로 선언하고 내부적으로 실제 동작을 맡은 private 가상 함수를 호출하는 식으로 구현해야 한다. 아래의 코드를 ..
[Effecitve C++] 34. 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
[Effecitve C++] 34. 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
2022.06.25public 상속의 두 가지 개념 1. 함수 인터페이스 상속 2. 함수 구현 상속 이 둘의 차이는 함수 선언과 함수 정의의 차이와 맥을 같이 한다. 아래의 예시를 보자. class Shape { public: virtual void draw() const = 0; // 순수 가상 함수 virtual void error(const std::string& msg); // 단순 가상 함수 int objectID() const; // 비가상 함수 }; class Rectangle: public Shape{}; class Ellipse: public Shape{}; Shape a; // 에러. 인스턴스를 만들 수 없음 Shape는 추상 클래스이다. 왜냐하면 멤버 함수인 draw()가 순수 가상 함수이기 때문이다...
[Effective C++] 33. 상속된 이름을 숨기는 일은 피하자
[Effective C++] 33. 상속된 이름을 숨기는 일은 피하자
2022.06.25변수의 유효 범위 int x; // 전역변수 void func() { double x; // 지역변수 cin >> x; // 지역변수 x에 값을 읽어 넣는다. } 컴파일러는 자신이 처리하고 있는 지역 유효 범위(local scope)를 뒤져서 같은 이름을 가진 것이 있는지 알아본다. 그리고 찾았다면 이외의 유효 범위에 대해서는 더 이상 탐색하지 않는다. 그리고 지역 유효 범위 안에 찾는 이름이 없다면 전역 유효 범위(global scope) 안에서 같은 이름을 가진 것을 찾는다. 상속과 변수의 유효 범위 class Base { private: int x; public: virtual void f1() = 0; virtual void f1(int); // 파생 클래스에 의해 가려짐 virtual void ..
[Effective C++] 32. public 상속 모형은 반드시 "is-a(..는..의 일종이다)"를 따르도록 만들자
[Effective C++] 32. public 상속 모형은 반드시 "is-a(..는..의 일종이다)"를 따르도록 만들자
2022.06.25이번 장은 간단하다. C++ 객체 지향에서 public 상속은 "is-a(..는..의 일종이다)"를 의미한다. Derived가 Base로부터 public 상속을 받았다면, "D is a B. 즉, D는 B의 일종이다. 하지만 B는 D의 일종이 아니다."라는 것이다. ex. 정사각형은 직사각형의 일종이다. 직사각형은 정사각형의 일종이 아니다. public 상속은 기본 클래스 객체가 가진 모든 것들이 파생 클래스 객체에도 그대로 적용된다고 단정 짓는 상속이다. 참고로 클래스간 맺을 수 있는 관계는 "is-a" 말고도 "has-a", "is-implemented-in-terms-of" 방식이 있다. 38, 39장에서 알아보자. 요약 public 상속의 의미는 "is-a(..는 ..의 일종)"이다. 기본 클래스..
[Effective C++] 31. 파일 사이의 컴파일 의존성을 최대로 줄이자
[Effective C++] 31. 파일 사이의 컴파일 의존성을 최대로 줄이자
2022.06.25컴파일 의존성을 줄이자는 의미 컴파일 의존성이란 #include 관계를 의미하고 이 #include 관계를 줄이자는 것이다. 줄여야 하는 이유 선언 한 헤더 파일 중 변경되는 것이 있다면 그 파일은 컴파일되어야 하며, 그럼 이렇게 컴파일된 파일과 연관된 다른 파일들이 모두 컴파일되고.... 컴파일 시간이 말도 못 하게 늘어날 것이다. 구현 세부사항을 따로 떼어서 지정하는 경우의 문제 namespace std { class string; } class Date; class Address; class Person { public: Person(const std::string& name, const Date& birthday, const Address& addr); std::string name() const..
[Effective C++] 30. 인라인 함수는 미주알고주알 따져서 이해해 두자
[Effective C++] 30. 인라인 함수는 미주알고주알 따져서 이해해 두자
2022.06.19인라인 함수의 장단점 인라인 함수는 함수 호출문을 그 함수의 본문으로 바꿔치기하는 것이다. 따라서 목적 코드의 크기가 커질 염려가 있고 메모리가 제한된 컴퓨터에서 아무 생각 없이 인라인을 남발했다가는 프로그램 크기가 그 기계에서 쓸 수 있는 공간을 넘어버릴 수도 있다. 가상 메모리를 쓰는 환경일지라도 인라인 함수로 인해 부풀려진 코드는 성능의 걸림돌이 되기 쉽다. 페이징 횟수가 늘어나고, 명령어 캐시 적중률이 떨어질 가능성도 높아진다. 그러나 본문 길이가 굉장히 짧은 인라인 함수를 사용하면, 함수 본문에 대해 만들어지는 코드의 크기가 함수 호출문에 대해 만들어지는 코드보다 작아질 수도 있다. 이렇게 되면 목적 코드의 크기도 작아지고 명령어 캐시 적중률도 높아진다. 인라인 함수에 대한 오해 인라인 함수의 ..