본문 바로가기
Object Oriented Programming(C++)/Effective C++

Effective C++ | 항목 34 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구분하자

by continue96 2023. 5. 21.

Effective C++ 항목 34

 

항목 34 인터페이스 상속과 구현 상속의 차이를 파악하고 구분하자

34.1 인터페이스 상속과 구현 상속

  • 멤버 함수 인터페이스 상속
    • 함수 인터페이스 상속은 함수 선언을 물려주는 것과 같다.
    • 부모 클래스의 멤버 함수 인터페이스는 is-a 관계에 의하여 항상 자식 클래스에게 물려준다.

 

  • 멤버 함수 구현 상속
    • 함수 구현 상속은 함수 정의를 물려주는 것과 같다.
    • 부모 클래스의 멤버 함수 구현은 멤버 함수의 세 가지 유형에 따라서 다르게 물려준다.
상속 순수 가상 함수 가상 함수 비가상 함수
인터페이스 상속 O O O
구현 상속 X O

 

 34.1.1 순수 가상 함수

  • 순수 가상 함수(pure virtual function)는 자식 클래스에게 함수의 인터페이스만 물려준다.
    • 일반적으로 부모 클래스에 있는 순수 가상 함수는 정의하지 않지만, 정의할 수는 있다.
    • 부모 클래스에 있는 순수 가상 함수는 자식 클래스에서 재정의(override)해야 한다.

 

 34.1.2 가상 함수

  • 가상 함수(virtual function)는 자식 클래스에게 함수 인터페이스와 기본적인 함수 구현을 물려준다.
    • 부모 클래스에 있는 가상 함수는 자식 클래스에서 재정의(override)할 수 있다.
  • 자식 클래스에서 가상 함수를 물려받고 싶지 않은 경우
    • 첫 번째, 가상 함수의 인터페이스(public 순수 가상 함수)와 구현(protected 비가상 함수)을 분리한다. 
    • 두 번째, 순수 가상 함수의 인터페이스를 정의한다.
// 첫 번째, 순수 가상 함수로 인터페이스를, 비가상 함수로 구현을 분리하여 호출한다.
class Airport { };

class Airplane {
public:
	virtual void Fly(const Airport& destination) = 0;

protected:
	void DefaultFly(const Airport& destination) { }
};

// 모델 A는 부모 클래스의 DefaultFly 비가상 함수를 호출합니다.
class ModelA : public Airplane {
public:
	virtual void Fly(const Airport& destination) override;
};

void ModelA::Fly(const Airport& destination) {
	DefaultFly(destination);
}

// 모델 B는 부모 클래스의 DefaultFly 비가상 함수를 호출하지 않습니다.
class ModelB : public Airplane {
public:
	virtual void Fly(const Airport& destination) override;
};

void ModelB::Fly(const Airport& destination) { }

int main(void) {
	Airport airport;
	Airplane* airplane = new ModelB;
	airplane->Fly(airport);
	return 0;
}

 

// 두 번째, 순수 가상 함수의 인터페이스를 정의한다.
class Airport { };

class Airplane {
public:
	virtual void Fly(const Airport& destination) = 0;
};

void Airplane::Fly(const Airport& destination) { }

// 모델 A는 부모 클래스의 Fly 순수 가상 함수를 호출합니다.
class ModelA : public Airplane {
public:
	virtual void Fly(const Airport& destination) override;
};

void ModelA::Fly(const Airport& destination) {
	Airplane::Fly(destination);
}

// 모델 B는 부모 클래스의 Fly 순수 가상 함수를 호출하지 않습니다.
class ModelB : public Airplane {
public:
	virtual void Fly(const Airport& destination) override;
};

void ModelB::Fly(const Airport& destination) { }

int main(void) {
	Airport airport;
	Airplane* airplane = new ModelB;

	airplane->Fly(airport);
	return 0;
}

 

 34.1.3 비가상 함수

  • 비가상 함수(non-virtual function)는 자식 클래스에게 함수 인터페이스와 필수적인 함수 구현을 물려준다.
    • 비가상 함수는 자식 클래스에서 재정의할 수 없다.
    • 비가상 함수는 상속과 상관없이 변하지 않는 동작, 즉 불변 동작을 물려받는다.

 

34.2 클래스 설계 실수

 34.2.1 모든 멤버 함수를 비가상 함수로 선언하기

  • 부모 클래스로 활용할 클래스는 가상 함수를 갖는 것이 바람직하다.
    • 가상 함수는 비가상 함수보다 비용이 더 들어가나, 프로그램 전체 성능에 영향을 줄 만큼 문제가 되지 않는다.
    • 80-20 법칙에 따라서 프로그램 전체 성능에 영향을 주는 20% 코드에 더 집중해야 한다.

 

 34.2.2 모든 멤버 함수를 가상 함수로 선언하기

  • 불변 동작이 필요하다면 부모 클래스는 비가상 함수를 갖는 것이 바람직하다.
NOTE
① public 상속에서 자식 클래스는 부모 클래스의 인터페이스를 항상 물려받는다.
② 순수 가상 함수는 인터페이스만 상속한다.
③ 가상 함수는 인터페이스 상속과 기본적인 구현을 상속한다.
④ 비가상 함수는 인터페이스 상속과 필수적인 구현을 상속한다.

댓글