일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- resource management class
- more effective c++
- reference
- 언리얼
- 함수 객체
- 다형성
- 상속
- 루아
- 암시적 변환
- lua
- 비교 함수 객체
- effective stl
- 오블완
- Smart Pointer
- 예외
- 스마트 포인터
- UE4
- implicit conversion
- virtual function
- exception
- 메타테이블
- 티스토리챌린지
- 영화
- 참조자
- c++
- 반복자
- operator new
- 게임
- Effective c++
- 영화 리뷰
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 34. 인터페이스 상속과 구현 상속의 차이 본문
728x90
항목 34 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
상속이 가지는 두 가지 의미
- 함수 인터페이스를 상속한다.
- 함수 구현을 상속한다.
이 두 가지 의미를 잘 헤아려서 사용해야 한다.
이에 대해 아래 예제를 통해 알아본다.
추상 클래스와 상속
// 추상 클래스
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 { ... };
- 추상 클래스의 인터페이스는 항상 상속되게 되어 있기 때문에 그 파생 클래스들에 지대한 영향을 끼친다.
- 위 예제를 보면 추상 클래스는 각 멤버함수를 다른 방식으로 정의했는데 이에 대해 정리해본다.
draw (순수 가상 함수)
- 순수 가상 함수의 특징
- 상속받는 구체 클래스는 이를 다시 선언해야 한다.
- 전형적으로 추상 클래스 안에서 정의를 갖지 않는다.
- 즉, 순수 가상 함수는 파생 클래스에게 함수의 인터페이스만 물려주기 위해서 사용한다.
- "draw 함수는 여러분이 직접 제공하도록 하시우. 하지만 당신이 어떻게 구현할지에 대해선 난 아무 생각 없소."
- 사실 순수 가상 함수도 정의를 제공할 수 있다.(컴파일 에러도 발생하지 않는다.)
- 단, 구현이 붙은 순수 가상 함수를 호출하려면 반드시 클래스 이름을 한정자로 붙여주어야 한다.
- 추상 클래스 인스턴스란 존재할 수 없으므로 당연한 이치.
-
Shape* ps = new Shape; Shape* ps1 = new Rectangle; ps1->draw(); // Rectangle::draw가 호출된다. ps1->Shape::draw(); // Shape::draw가 호출된다.
error (단순 가상 함수)
- 파생 클래스에서 인터페이스에 더해 오버라이드 할 수 있는 함수 구현부도 제공한다.
- "error 함수는 여러분이 지원해야 한다우. 그러나 굳이 새로 만들 생각이 없다면 Shape 클래스에 있는 기본 버전을 그냥 쓰시구려."
- 단순 가상 함수가 인터페이스 + 구현을 제공하는 것이 위험할 때도 있다.
-
class Airport { ... }; // 공항 클래스 class Airplane { public: virtual void fly(const Airport& destination) { // 주어진 목적지로 비행기를 날려 보내는 기본 동작 코드 } ... }; class ModelA : public Airplane { ... }; class ModelB : public Airplane { ... }; // 기존 비행기들과 전혀 다른 방식으로 나는 비행기가 도입된다. class ModelC : public Airplane { // 허나 실수로 fly 함수가 선언되지 않았다! }; Airport PDX {...}; Airplane* pa = new ModelC; ... pa->fly(PDX); // Airplane::fly 함수가 호출되는 문제!!!
- 이처럼, 파생 클래스에서 요구하지 않았는데도 기능이 제공되므로 실수의 여지가 있다.
-
- 가상 함수의 구현이 강제로 상속되는 문제의 해결법
- 기본 동작을 원한다고 명시적으로 밝힐 때만 기능을 물려주도록 하면 된다.
- 순수 가상 함수로 구현하고, 기본 구현을 별도의 함수로 제공한다.
-
class Airplane { virtual void fly(const Airport& destination) = 0; // 순수 가상 함수로 변경 ... protected: void defaultFly(const Airport& destination); // 파생 클래스로 기능 제공 };
- 순수 가상 함수의 구현부 제공이 가능하다는 점을 활용하여 따로 기본 구현용 함수를 준비하지 않는 방법도 가능하다.
- 허나 이 경우 보호 수준이 날아간다. fly는 public 이므로, protected로 기본 구현용 함수를 따로 제공하는 것보다는 보호 수준이 낮다.
objectID (비가상 함수)
- 비가상 함수를 선언하는 목적은, 파생 클래스가 함수 인터페이스 + 필수적인 구현을 물려받게 하는 것이다.
- 필수적인 구현이란, 클래스 파생에 상관 없이 변하지 않는 동작이다.
- "Shape 및 이것에서 파생된 모든 객체는 객체의 식별자를 내어 주는 함수를 갖게 되겠지. 객체 식별자를 계산하는 방법은 항상 똑같겠군. 실제 계산 방법은 Shape::objectID의 정의에서 결정되고, 파생 클래스는 이것을 바꿀 수 없겠는걸."
- 비가상 함수는 클래스 파생에 상관없는 불변동작과 같으므로 절대로 파생 클래스에서 재정의할 수 없다.(항목 36 참조)
멤버 함수 선언 시 결정적인 실수
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 36. 상속된 비가상 함수 이름 가리기 문제 (0) | 2024.07.05 |
---|---|
[Effective C++] 35. 일반 가상 함수 외의 구현법 (0) | 2024.07.04 |
[Effective C++] 33. 상속된 이름 가리기 문제 (0) | 2024.07.02 |
[Effective C++] 32. public 상속은 "is-a" (0) | 2024.06.28 |
[Effective C++] 31. 컴파일 의존성 줄이기 (0) | 2024.06.27 |
Comments