본문 바로가기

서적 정리/Effective C++56

37.어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자 가상 함수는 동적 바인딩(dynamically binding)이고 비가상 함수는 정적 바인딩(static binding)이다. 기본 매개변수 값을 가진 가상 함수를 상속하는 경우 가상 함수는 동적 바인딩하지만, 기본 매개변수 값은 정적 바인딩을 한다. class Shape { public: enum ShapeColor { Red, Green, Blue }; virtual void Draw(ShapeColor color = ShapeColor::Red) const = 0; }; class Rectangle : public Shape { virtual void Draw(ShapeColor color = ShapeColor::Green) const override; //기본 매개변수 있음 }; class Ci.. 2021. 12. 22.
36.상속받은 비가상 함수를 파생 클래스에서 재정의하는 것은 절대 금물! 이 챕터 많이 기다렸다. 당연시하게 기본 클래스의 비가상 함수는 파생 클래스에서 재정의하지 않았다. 하지만 이 책의 33장에서 C++ 이름 가리기를 보면서 비가상 함수를 재정의해도 사용은 될 텐데? 싶어서 테스트해보니 아무문제 없이 컴파일 됐고 문제없이 작동한다. 왜 금지하는지 알아보자. 기본 클래스로 Base라는 클래스를 만들고 Base 클래스 안에 비가상 함수 mf 함수를 선언하고 Base 클래스를 public 상속하는 Derived 클래스에서 mf 함수를 재정의한다고 가정하자. class Base { public: void mf() { cout 2021. 12. 22.
35.가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자 순수 가상 함수가 아닌 가상 함수로 선언되어 있다는 것은 기본 구현이 제공된다는 사실을 알 수 있다(34장 참고). 가상 함수는 반드시 private 멤버로 둬야 한다고 주장하는 가상 함수 은폐론이 있다고 한다. 난 이해가 안간다. 가상 함수라면 파생 클래스에서 오버라이드 해서 사용할 수 있지만, 기본 클래스의 구현된 가상 함수를 쓸 경우엔 파생 클래스의 객체에서는 사용하지 못하니 무조건 오버라이드 하란 소린데? 가상 함수 은폐론자들이 제안하는 설계방법이 있다. 비가상 인터페이스(non-virtual interface : NVI) 관용구를 통한 템플릿 메서드 패턴(Template Method) 템플릿 메서드 패턴은 C++에서 사용하는 템플릿과는 무관한 이름이니 참고하자. 가상 함수를 public 멤버 함.. 2021. 12. 21.
34.인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자 public 상속은 두 가지로 나눌 수 있다. 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 { }; 순수 가상 함수인 Draw 함수로 인해 Shape 클래스는 추상 클래스가 되었고, Rectangle과 Ellipse .. 2021. 12. 21.
33.상속된 이름을 숨기는 일은 피하자 유효범위(scope)는 변수의 수명을 의미한다. 지역변수, 전역변수 등 변수의 유효범위는 다르다. 만약 유효범위가 다른 변수의 이름이 같다면 어떤일이 발생할까? int x; //전역 변수 void SomeFunc() { int x; //지역 변수 cin >> x; //지역 변수 x에 값을 넣음 } 컴파일러가 SomeFunc의 유혀범위 안에서 x라는 이름을 읽으면, 컴파일러는 자신이 처리하고 있는 유효범위를 찾아 같은 이름을 가진 것이 있는가 찾아본다. 현재 SomeFunc에서 x라는 이름이 있기 때문에 이 외의 유효범위에 대해서는 더이상 탐색하지 않는다. 즉, SomeFunc 함수의 지역변수 x에 의해 전역 변수 x는 이름이 가려진다. 상속에서로 주제를 다시 잡고 생각해보자. 기본 클래스에 속해 있는 것.. 2021. 12. 20.
32.public 상속 모형은 반드시 "is-a(...는 ...의 일종이다)"를 따르도록 만들자 파생 클래스가 기본 클래스를 상속할 때 키워드로 public, private, protected를 사용한다. pubic 상속은 is-a(...는 ...의 일종이다)를 의미한다는 것을 기억하자. private 상속은 의미 자체가 완전히 다르다(39장 참고). protected는 요즘 의미가 애매하다고 하다. 간단한 애기인데 매개변수에 기본 클래스의 객체나 포인터 참조자를 요구하는 함수는 파생 클래스의 객체 포인터, 참조자도 받아 들일 수 있다. 즉, 파생 클래스는 기본 클래스의 매개변수에 들어갈 수 있다. 반대로 기본 클래스는 파생 클래스의 매개변수에 들어갈 수 없다. class Person { }; class Student : public Person { }; void Eat(const Person& p.. 2021. 12. 20.
31.파일 사이의 컴파일 의존성을 최대로 줄이자 C++은 인터페이스와 구현을 깔끔하게 분리하는 일에는 일가견이 없다고 한다. C++의 클래스 정의(class definition)는 클래스 인터페이스만 지정하는 것이 아니라 구현 세부사항까지 지정하고 있기 때문이다. 세부사항이란 클래스 안에 선언된 객체들 같은 것들을 말하는것 같다. 클래스에서 STL이든 직접 만든 클래스를 사용하려면 #include를 해야한다. 이번 챕터의 주인공은 #include 문으로 인해 헤더 파일들 사이에 컴파일 의존성(compliation dependency)이다. Person 클래스에 Date와 Address 파일이 포함되어 있다고 생각해보자. #include #include "Date.h" #include "Address.h" class Person { public: Per.. 2021. 12. 20.
30.인라인 함수는 미주알고주알 따져서 이해해 두자 사실 나는 인라인 함수를 잘 안쓴다. 구현하기 바빠서 그런 것도 있지만 필요성을 못느낀다. 비슷한 의미로 람다 함수도 손이 안간다. 장점보단 단점이 크게 보이기 때문이다. 코드가 함수로 점프하는게 아닌 그냥 가져와서 빠르다고는 하지만, 코드크기도 부풀려지고 굳이란 생각도 있다. 무엇보다 솔직히 언제 써야 최고의 성능을 발휘하는지도 모른다. 인라인 함수는 함수처럼 보이고 함수처럼 동작하고 매크로보다 안전하고(2장 참고) 함수 호출 시 발생하는 오버헤드 걱정도 없는 장점이 있다. 또한 컴파일러 최적화는 함수 호출이 없는 어찌보면 절차적 언어 같은 코드에 적용되도록 설계되었기 때문에, 함수 본문에 대해 문맥별(context-specific) 최적화를 걸기 용이해 진다. 실제 대부분의 컴파일러는 아웃라인(out.. 2021. 12. 19.
29.예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자! 예외 안전성(exception safety)은 코드 설계에 있어서 힘든 일이지만, 코딩을 하면 할수록 이런 상황때문에 터지던데.. 하면서 예외 처리를 생각하는 것이 당연시하고 있다. 배경그림이 나오는 GUI 메뉴를 구현하는 클래스를 만든다고 가정하자. class PrettyMenu { public: void ChangeBackground(std::istream& imgSrc); //배경을 바꾸는 멤버 함수 private: Mutex mutex; Image* bgImage; //현재 배경그림 int imageChanges; //배경그림이 바뀐 횟수 }; void PrettyMenu::ChangeBackground(std::istream& imgSrc) { lock(&mutex); //뮤텍스 잠금 delet.. 2021. 12. 19.