728x90

Multiple Inheritance (다중 상속)으로 인해 Dianmond Problem (다중 상속 문제)를 야기할 수 있는데 Dianmond Problem과 해당 문제를 해결하는 방법에 대해서 배워보자. 

 

 

Diamond Problem (다중 상속 문제)

 

Diamond Problem은 다중으로 상속을 했을 때 나타나는 문제이다. 

하이브리드 동물 Liger를 클래스로 나타낸다면 아래 그림과 같을 것이다. 

위 그림처럼 Tiger와 Lion은 둘 다 동물이기 때문에 Animal클래스를 상속을 받고 Tiger와 Lion의 교배로 낳은 Liger는 두 동물의 상속으로 만들어진다. 위 그림을 코드화 시키면 아래와 같다. 

#include <iostream>

class Animal
{
public:
	Animal()
	{
		std::cout << "Animal Constructor\n";
	}
	virtual ~Animal() = default;
private:
	double animalData;
};

class Lion : public Animal
{
public:
	Lion()
	{
		std::cout << "Lion Constructor\n";
	}
	virtual ~Lion() = default;
private:
	double lionData;
};

class Tiger : public Animal
{
public:
	Tiger()
	{
		std::cout << "Tiger Constructor\n";
	}
	virtual ~Tiger() = default;
private:
	double tigerData;
};

class Liger : public Lion, public Tiger
{
public:
	Liger()
	{
		std::cout << "Liger Constructor\n";
	}
	virtual ~Liger() = default;
private:
	double ligerData;
};

int main()
{
	Liger miniLiger;
}

위 코드를 보면 딱히 문제점이 없어 보인다. 하지만 Liger객체를 생성하여 생성자가 호출이 되는 순서를 보게 되면 이상한 현상이 일어나게 된다. 

코드 출력

출력 화면을 보게 되면 Animal의 생성자가 2번 호출이 되는 것을 확인할 수 있다. 사실 쉽게 생각하면 Tiger와 Lion은 각각 Animal을 상속을 받았기 때문에 Animal object를 가지게 되고 또 Liger는 Tiger와 Lion을 다중 상속을 받았기 때문에 Animal 객체가 두 번 생성이 되는 것이다.  또 다른 문제는 만약 Liger 객체를 위와 같이 생성을 했다고 가정을 하면 Animal에 animalData를 접근하는 멤버 함수를 만들고 animalData를 접근하려고 한다면 생성된 두 개의 Animal객체 중 어떤 Animal객체의 멤버 함수를 호출하는가라는 문제가 또 생긴다. 

 

이러한 현상을 방지하기 위해 우리는 Virtual Inheritance라는 것을 사용하면 위 문제를 해결할 수 있다. 

 

 

Virtual Inheritance (가상 상속)

 

가상 상속이란 클래스 상속에서 "Virtual"키워드를 통하여 가상으로 상속을 받게 하는 방법이다. Virtual Function에서 Derived클래스에서 해당 Virtual Function에 대한 재정의를 기대한다고 말했는데 Virtual Inheritance에서도 동일하게 다른 클래스에서 해당 Base클래스를 상속받을 것을 기대한다고 보면 된다. 만약 다른 클래스에서 해당 Base클래스를 상속받지 않았을 경우는 자기가 받으면 문제는 해결이 된다. 

 

위에 코드를 가상 상속으로 수정한 코드는 아래와 같다. 

#include <iostream>

class Animal
{
public:
	Animal()
	{
		std::cout << "Animal Constructor\n";
	}
	virtual ~Animal() = default;
private:
	double animalData;
};

class Lion : virtual public Animal
{
public:
	Lion()
	{
		std::cout << "Lion Constructor\n";
	}
	virtual ~Lion() = default;
private:
	double lionData;
};

class Tiger : virtual public Animal
{
public:
	Tiger()
	{
		std::cout << "Tiger Constructor\n";
	}
	virtual ~Tiger() = default;
private:
	double tigerData;
};

class Liger : public Lion, public Tiger
{
public:
	Liger()
	{
		std::cout << "Liger Constructor\n";
	}
	virtual ~Liger() = default;
private:
	double ligerData;
};

int main()
{
	Liger miniLiger;
}

위와 같이 virtual 키워드를 통해서 Diamond Problem을 해결할 수 있다. 

 

 

Virtual Inheritance를 통해서 상속을 받게 되면 기존 상속과 Memory Layout이 다르다. thunk함수와 offset을 이용하여 맞는 함수를 가리키게 되는데 이 부분은 복잡하고 또한 Virtual Inhertance자체가 잘 사용이 되지 않는 기능이기 때문에 해당 Memory Layout은 영상을 참고하여 한번 보는 것으로 끝낸다. 궁금하다면 하단 레퍼런스에서 링크를 통해 보길 바란다. 

 

 

Ref.

https://www.youtube.com/watch?v=0izSeUXpwDw&list=PLDV-cCQnUlIar6Wx3rkXHs7wbfmlz34Fz&index=7&t=300s 

+ Recent posts