[ Effective C++ ] 정리 모음집
" C++ 프로그래머의 필독서, 스콧 마이어스의 Effective C++ 를 읽고 내용 요약 / 정리 "
" 순수 가상, 가상, 비가상 함수를 사용목적에 맞게 사용해 클래스를 구성하자! "
- 인터페이스 상속은 구현 상속과 다르다!
- public 상속에서 파생 클래스는 항상 기본 클래스의 인터페이스를 모두 물려 받는다!
- 순수 가상 함수는 인터페이스 상속만을 허용한다!
- 단순 가상 함수는 인터페이스 상속과 더불어 기본 구현의 상속도 가능하도록 지정한다!
- 비가상 함수는 인터페이스 상속과 더불어 필수 구현의 상속도 가하도록 지정한다!
class Shape
{
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
...
};
class Rectangle : public Shape { ... };
class Ellipse : public Shape { ... };
virtual void draw() const = 0;
📢 순수 가상 함수도 정의를 제공할 수 있다!
Shape* ps = new Shape;
Shape* ps1 = new Rectangle;
ps1->draw();
Shape* ps2 = new Ellipse;
ps2->draw();
ps1->Shape::draw();
ps2->Shape::draw();
- 사용 시 ::
(스코프 연산자)를 붙여줘야 한다
- 단순 가상 함수에 대한 기본 구현을 보다 안전하게 제공하는 메커니즘으로도 활용 된다
virtual void error(const string& msg);
class Airplane
{
public:
virtual void fly();
...
};
void Airplane::fly()
{
// 비행기의 기본 날기 동작
}
class ModelA : public Airplane { ... };
class ModelB : public Airplane { ... };
class ModelC : public Airplane // 특별한 날기 동작을 하는 C 비행기
{
... // 하지만 fly() 함수가 선언되지 않음
}
📢 해결 방법 1 - 가상 함수의 인터페이스와 기본 구현의 연결관계 끊기
class Airplane { public: virtual void fly() = 0; ... protected: void defaultFly(); }; void Airplane::defaultFly() { // 비행기의 기본 날기 동작 } class ModelA : public Airplane // B도 마찬가지 { public: virtual void fly() { defaultFly(); } ... }; class ModelC : public Airplane { public: virtual void fly(); ... }; void ModelC::fly() { C 비행기만의 날기 동작 }
- fly 함수를 순수 가상 함수로 만들기
- 파생 클래스 에서는 해당 함수의 구현을 할 수 밖에 없어진다- 기존의 기본 버전 함수는 defaultFly 라는 비가상 함수로 만듬
- 기존 동작 방식을 원하는 A, B 비행기는 fly 호출 시 defaultFly를 호출하게
- C 비행기는 자신만의 동작을 fly에 구현
📢 defaultFly를 가상 함수로 두면 똑같은 문제 발생...
- 파생 클래스에서 재정의를 안한다면...
📢 해결 방법 2 - 순수 가상 함수의 정의를 제공하기
class Airplane { public: virtual void fly() = 0; ... }; class ModelA : public Airplane // B도 마찬가지 { public: virtual void fly() { Airplane::fly(); } ... }; class ModelC : public Airplane { public: virtual void fly(); ... }; void ModelC::fly() { C 비행기만의 날기 동작 }
- fly 함수를 순수 가상 함수로 만들고 정의도 제공
- A, B 비행기 에서는 스코프 연산자를 통해Airplane::fly()
호출
- C 비행기는 자신만의 동작을 fly에 구현
int objectID() const;
비가상 소멸자가 문제가 될 수 있다
- [ 항목 7 : 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 ] 참조
파생 클래스가 기본 클래스의 동작에서 특별하게 만들어질 여지가 없어진다
- 클래스 파생을 처음 부터 염두에 두지 않았다면 상관 없음
📢 가상 함수의 비용만 생각해서 쓰지 않으려고 하는 생각은 버리자!
📢 물론 맞을 수도 있다!
- 인터페이스 클래스의 경우...
- [ 항목 31 : 파일 사이의 컴파일 의존성을 최대로 줄이자 ] 참조