카테고리:

다중 상속 (Multiple Inheritance)

다중 상속은 둘 이상의 클래스를 상속 받는 것을 의미합니다.

다중 상속을 통해 다양한 기능을 가진 클래스를 쉽게 구현할 수 있지만, 다중 상속에는 명확한 단점이 존재하기 때문에 실제로는 자주 사용되지 않습니다.

다음은 다중 상속의 예시입니다

class Animal
{
public:
	void Speak();
};

class Dog : public Animal
{
public:
	void Eat();
};

class Bird : public Animal
{
public:
	void Eat();
};

class DogBird : public Dog, public Bird
{

};

위와 같이 Dog와 Bird를 상속 받는 DogBird 클래스가 바로 다중 상속입니다.

현실의 개념으로 본다면 큰 문제는 없어 보이지만 프로그래밍에서는 다이아몬드 문제라는 문제가 발생합니다.

다이아몬드 문제

상속 그래프

다중 상속 받는 형태가 마치 다이아몬드와 같다하여 붙여진 이름입니다.

다이아몬드 문제에서 발생하는 점은 바로 ‘모호함’ 입니다.

DogBird에서 Eat을 호출하는 경우를 고려한다면 컴파일러는 이 Eat이 Dog 클래스의 Eat인지 Bird 클래스의 Eat인지 알 수 없습니다.

또, DogBird에서 Animal 클래스의 Speak를 호출한다면, 모호하다는 오류가 발생합니다.

상속 그래프에선 별 문제가 없어 보이는 듯 하지만, 실제로는 Dog를 통해 상속 받은 Animal인지 Bird를 통해 상속 받은 Animal인지 알 수 없기 때문에 모호함이 발생합니다.

int main()
{
  DogBird db;

  db.Eat(); // 모호함!
  db.Speak(); // 모호함!
  return 0;
}

Eat과 Speak을 호출하기 위해서는 어떤 클래스의 메서드인지 명시적으로 호출해주어야 합니다.

int main()
{
  DogBird db;

  db.Dog::Eat(); // Dog 클래스의 Eat 호출!
  db.Bird::Eat(); // Bird 클래스의 Eat 호출!
  db.Dog::Speak(); // Dog 클래스를 통해 상속받은 Animal의 Speak 호출!
  db.Bird::Speak(); // Bird 클래스를 통해 상속받은 Animal의 Speak 호출!

  return 0;
}

해결법

다이아몬드 문제의 해결 방법은 두 가지 입니다.

첫 번째는 아주 간단하게 설계 과정에서 다중 상속을 피하는 것입니다.

JAVA와 Unreal은 아예 다중 상속을 사용하지 못하도록 막아 문제 발생 자체를 예방 하였습니다.

두 번째는 Virtual 상속을 사용하는 것 입니다.

Dog와 Bird가 Animal을 상속 받을 때 virtual 키워드를 통해 DogBird가 상속 받게 될 Animal을 하나로 통합시킨다면 모호함이 발생하지 않습니다.

class Animal
{
public:
	void Speak();
};

class Dog : virtual public Animal
{
public:
	void Eat();
};

class Bird : virtual public Animal
{
public:
	void Eat();
};

class DogBird : public Dog, public Bird
{

};

int main()
{
  DogBird db;

  db.Speak(); // 오류가 발생하지 않습니다

  return 0;
}

여러 클래스의 다중 상속은 득보다 실이 많은 이유로 기피 대상이지만, 하나의 클래스에 여러 개의 인터페이스를 상속 받는 방법은 많이 사용되는 방법이기 때문에 다중 상속 자체가 쓰이지 않는다고 보기는 어렵습니다.