[Effective C++] #34 인터페이스 상속, 구현 상속, 차이점
Scott Meyers의 "Effective C++"를 통해, C++ 구현에 필요한 개념들을 이해하고, 기록하기 위함입니다. 해당 항목은 6장 "상속, 그리고 객체 지향 설계", 항목 34 "인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자"에 해당하는 내용입니다.
인터페이스 상속, 구현 상속
class Shape
{
public:
virtual void draw() const = 0; //순수 가상 함수
virtual void error(const std::string& msg); //단순 가상 함수
int objectID() const; //비가상 함수
...
};
class Rect : public Shape{...};
class Triangle: public Shape{...};
C++의 객체지향 설계를 위해 "상속"의 개념은 매우 중요합니다. 이번 항목을 통해 부모 클래스의 인터페이스만 상속받는 경우와 인터페이스, 그리고 구현 둘 다 상속받는 경우에 대해서 알아보겠습니다. 위 예제 코드는 위에서부터 순수 가상 함수, 단순 가상 함수, 그리고 비 가상 함수를 갖는 인터페이스 클래스를 보여줍니다.
순수 가상 함수, 인터페이스 상속
public:
virtual void draw() const =0;
Shape* p1 = new Shape;
Shape* p2 = new Rect;
// 추상 클래스의 순수 가상 함수를 사용하기위해 "::" 지정자가 필수
p1->Shape::draw();
"Shape"의 순수 가상 함수는 파생 클래스들의 그리기 동작을 위한 순수 가상 함수를 갖고 있습니다. "순수 가상 함수"의 존재는 곧 해당 클래스는 추상 클래스라는 것을 의미합니다. 따라서, Shape 클래스의 인스턴스화는 불가능하며, 파생 클래스들은 멤버 함수 인터페이스를 항상 상속하게 됩니다. 더불어, 순수 가상 함수를 선언하는 목적은 파생 클래스에게 인터페이스만을 물려주며, 순수 가상 함수의 구현을 요구합니다. 간단하게 정리하면, 추상 클래스의 순수 가상 함수의 선언은 파생 클래스에게 인터페이스만을 물려주기 위함입니다. 덧붙이면, 순수 가상 함수에 정의를 제공할 수 있지만, 호출을 위해서 클래스 이름을 한정자로 붙여 주어야만 합니다.
단순 가상 함수, 인터페이스 및 구현 상속
public:
virtual void error(const std::string& msg);
단순 가상 함수는 순수 가상 함수와 다른 점을 갖고 있습니다. 하나는 파생 클래스로 하여금 구현부를 물려받아 오버라이드 할 수 있게 해 줍니다. 정리하면, 단순 가상 함수의 경우, 인터페이스뿐만 아니라 함수의 기본 구현도 파생 클래스에게 제공합니다. 예를 들면, "error 함수의 구현을 별도로 작성하세요, 귀찮다면, Shape 클래스의 기본 구현을 사용해도 좋습니다"라고 얘기할 수 있겠죠.
비 가상 함수, 인터페이스 및 구현 상속 (오버라이드 불가)
class Airplane
{
public:
virtual void fly(const Airport& destination) = 0; //순수 가상 함수
protected:
void defaultFly(const Airport& destination); //비 가상 함수
};
// 기본 fly 동작을 사용하길 원하는 경우
class ModelA : public Airplane
{
public:
virtual void fly(const Airport& destination)
{
return defaultFly(destination);
}
};
// 기본 fly 동작이 아닌, 별도의 Fly 동작을 원한는 경우
class ModelB : public Airplane
{
public:
virtual void fly(const Airport& destination);
...
};
void ModelB::fly (const Airport& destination)
{
//ModelB만의 특수한 Fly 구현부 정의
}
Airplane 클래스는 순수 가상 함수와 비 가상 함수를 파생 클래스에게 제공합니다. 이때, 각 공항의 비행기의 비행 동작을 위한 "fly"(순수 가상 함수)는 인터페이스만을 제공하므로, 별도의 구현부를 각 파생 클래스에서 작성해야 합니다. 따라서, 기본 동작에 대한 상속을 강제하는 것을 방지할 수 있습니다. 쉽게 말해, Airplane 인터페이스 클래스에서 작성한 구현 내용을 구체 클래스들은 무조건 따르지 않아도 된다는 말이죠. 반면에, "deafaultFly"(비 가상 함수)는 인터페이스와 구현부를 동시에 제공하고 있습니다. 더불어, 가상 함수가 아닌 일반 멤버 함수이기 때문에, 파생 클래스의 오버라이드를 제한합니다. 따라서, "defaultFly"는 파생 클래스에서 재정의 될 수 없고, 기본 구현 내용을 강제합니다. 정리하면, 비 가상 함수 선언의 목적은 파생 클래스가 함수 인터페이스와 함수의 필수적인 구현을 상속받습니다.
순수 가상 함수 = 인터페이스 상속, 단순 가상 함수 = 인터페이스와 기본 구현 상속, 비 가상 함수 = 인터페이스와 필수 구현 상속
'언어 > Effective C++' 카테고리의 다른 글
[Effective C++] #36 비 가상 함수의 상속, 바인딩 (0) | 2022.02.04 |
---|---|
[Effective C++] #35 Public 가상 함수의 대안, NVI, 전략 패턴 (0) | 2022.02.03 |
[Effective C++] #23 비 멤버, 비 프렌드 함수 (0) | 2022.01.28 |
[Effective C++] #22 클래스 데이터 멤버, 접근 제어, 접근 지정자 (0) | 2022.01.28 |
[Effective C++] #21 함수의 객체 반환, 참조자 반환 (0) | 2022.01.27 |