All
[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인라인 함수의 장단점 인라인 함수는 함수 호출문을 그 함수의 본문으로 바꿔치기하는 것이다. 따라서 목적 코드의 크기가 커질 염려가 있고 메모리가 제한된 컴퓨터에서 아무 생각 없이 인라인을 남발했다가는 프로그램 크기가 그 기계에서 쓸 수 있는 공간을 넘어버릴 수도 있다. 가상 메모리를 쓰는 환경일지라도 인라인 함수로 인해 부풀려진 코드는 성능의 걸림돌이 되기 쉽다. 페이징 횟수가 늘어나고, 명령어 캐시 적중률이 떨어질 가능성도 높아진다. 그러나 본문 길이가 굉장히 짧은 인라인 함수를 사용하면, 함수 본문에 대해 만들어지는 코드의 크기가 함수 호출문에 대해 만들어지는 코드보다 작아질 수도 있다. 이렇게 되면 목적 코드의 크기도 작아지고 명령어 캐시 적중률도 높아진다. 인라인 함수에 대한 오해 인라인 함수의 ..
[Effective C++] 29. 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!
[Effective C++] 29. 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!
2022.06.19예외 안전성의 조건 소프트웨어 시스템은 예외에 안전하거나, 예외에 뚫려있거나 둘 중 하나이다. 아래의 예시를 보자. class PrettyMenu{ public: // 배경그림을 바꾸는 멤버함수 void changeBackground(std::istream& imgSrc); private: Mutex mutex; // 이 객체 하나를 위한 뮤텍스 Image *bgImage; // 현재의 배경그림 int imageChanges; // 배경그림이 바뀐 횟수 }; void PrettyMenu::changeBackground(std::istream& imgSrc) { lock(&mutex); // 뮤텍스 획득 delete bgImage; // 이전 배경그림 없앰 ++imageChanges; // 그림 변경횟수 ..
[Effective C++] 28. 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
[Effective C++] 28. 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자
2022.06.18예시로 사각형 클래스로부터 시작해보자. 메모리 효율을 높이기 위해 꼭짓점(Point)을 별도의 구조체(RectData)에 넣어 사각형 클래스(Rectangle)가 이를 가리키도록 해보자. class Point { public: Point(int x, int y); void setX(int newVal); void setY(int newVal); }; struct RectData { Point ulhc; // 좌측상단 Point lrhc; // 우측하단 }; class Rectangle { private: std::tr1::shared_ptr pData; public: // 아래 두행은 전부 상수멤버함수임을 기억하자. Point & upperLeft() const { return pData->ulhc; }..
[Effective C++] 27. 캐스팅은 절약, 또 절약! 잊지 말자
[Effective C++] 27. 캐스팅은 절약, 또 절약! 잊지 말자
2022.06.18'어떤 일이 있어도 type error가 생기지 않도록 보장한다.'라는 것은 C++의 동작 규칙이다. 즉, 이론적으로는 컴파일만 잘 되면 그 이후엔 어떤 객체에서도 불완전한 연산이나 말도 안 되는 연산 등을 수행하지 않는다는 것이다. 하지만 C++에서는 cast 시스템 때문에 이런 보장이 깨질 수도 있다. 캐스트 방법 C 스타일의 캐스트 (구형 캐스트) (T) 표현식 T(표현식) : 함수 방식의 캐스트 C++ 스타일의 캐스팅 const_cast(표현식) 객체의 상수성을 없애는 용도, const -> non-const로 바꾸는 용도 dynamic_cast(표현식) 안전한 다운 캐스팅을 할 때 사용한다. 상속 상황에서 쓰이며, 주어진 객체가 어떤 클래스 상속 계통에 속한 특정 타입인지 아닌지를 결정하는 작업..