Part2::Ch 03. 상속 - 06. 다중 상속
다중 상속도 이전에 배웠던 private, protected 상속과 같이 조심해서 사용해야하는 상속 중 하나이다.
먼저 부모 클래스가 될 클래스를 두개 정의해보자.
그리고 이걸 상속 받을 자식 클래스를 만들어주고
이제 두 부모 클래스를 한번에 상속 해주자.
한번에 상속하는 방법은 그냥 ,(쉼표)를 기준으로 두개의 부모클래스를 작성해주면 된다.
이렇게 되면 두 부모의 모든걸 받아오게 될 것이다.
그럴때 한번 몇가지 확인해볼 것이 있는데 먼저 두개의 부모가 서로 같은 명칭의 멤버를 가지는 경우이다.
이 경우에는 서로 부모가 이름을 가리게 된다.
이전에는 상속받은 자식이 부모의 이름을 가리는 경우였는데 이번엔 부모끼리 이름을 가리는 경우가 생기는 것이다.
이렇게 되면 사용 할때
이렇게 모호하다고 알려준다.
어떤걸 써야할지 모르겠다고 컴파일러가 알려주는 것이다.
자식이 가릴때에는 그냥 자식꺼를 가져오면 되니까 컴파일러가 따로 구분없이 자식거를 가져오게 하는데
부모의 경우는 다른것이다.
이걸 그냥 쓰려면
이런식으로 각각 어떤 범위에 포함된것을 자식에서 사용한다고 알리면 되긴 하나 이런식으로 쓰는 설계 방식은 하나 두개정도는 괜찮으나 값이 많아질수록 혼란스러워질 것이다.
그래서 이렇게 다중상속을 하는 경우에는 실제 사용하는 쪽에서
이런식으로 어떤 부모에서 가져온 기능을 사용할지 작성해서 해결할 수 있다.
추가로 업 캐스팅을 사용해서
이런식으로 구분할 수 도 있긴하다.
그런데 이렇게 사용하면 꽤나 번거롭긴하다.
이것보다 더 큰 다중 상속의 문제점은 다이아몬드 상속이란 것인데
Parent
↗ ↖
ChildrenA ChildrenB
(Parent를 가짐) (Parent를 가짐)
↖ ↗
GrandChildren
(ChildrenA(+Parent) + ChildrenB(+Parent)를 가짐)
이런 형태로 상속이 구현되었을때 GrandChildren은 ChildrenA와 ChildrenB를 상속하면서 Parent를 ChildrenA가 갖고 있는 Parent, ChildrenB가 갖고 있는 Parent 둘 다 받아 오게 된다.
이걸 코드로 한번 보면
이런 형태를 가지는 것이다.
각각에 생성자를 생성해서 마지막 GrandChildren을 호출했을때 어떤 객체를 생성해주는지 확인해보면 먼저 가장 상위의 Parent객체에 인자를 하나 받는 생성자를 하나 생성해주고
각각의 중간 ChildrendA, B에서는 디폴트 생성자를 통해서 각 부모 생성자를 호출해주도록 하고 값을 서로 다른 값을 선택해서 호출해주자.
그리고 이제 가장 마지막 GrandChildren에서 기본 생성자를 하나 생성해주자.
상속에서 자식을 객체로 생성할때 부모 생성자를 호출해 부모도 객체로 생성하는데 만약 자식 생성자에서 따로 부모 생성자에 대한 언급이 없다면 부모의 디폴트 생성자를 호출하게 된다.
여기서는 GrandChildren의 경우 따로 생성자를 선언하지 않았기에 부모의 디폴트 생성자를 호출하고 부모인 ChildrenA와 B의 경우는 만약 부모 생성자인 Parent의 생성자를 언급하지 않는다면 Parent의 디폴트 생성자를 호출할텐데 명확하게 ChildrenA에서 호출했는지 B에서 호출했는지를 알기 위해서 Parent에 디폴트 생성자 말고 인자를 받는 생성자를 생성했고 각각의 자식에서 부모 생성자를 언급하여 각각 어떤 값을 던져줬는지를 보고 어떤 자식 생성자에서 부모생성자를 호출했는지를 파악하는 코드라고 보면 된다.
이제 GrandChildren을 생성해주면
보이다 싶이 GrandChildren 객체를 생성했는데 Parent가 두번 불리면서 두번 생성된것을 볼 수있다.
만약 이때 Parent에 멤버 변수나 함수가 있을때 GrandChildren을 통해서 호출한다면
어디에 있는 멤버 변수, 함수인지를 모르게 된다.
왜냐면 ChildrenA와 B가 각각 Parent를 갖기에
이렇게 중간 부모를 구분해줘야만 Parent에 있는 멤버 변수, 함수를 사용할 수 있게 된다.
또한 업 케스팅을 사용할 때에도
GrandChildren를 상속하는 클래스 ChildrenA와 B중 누가 갖고 있는 Parent를 기준으로 업케스팅 할것인지가 명확하지 않기 때문에 모호하다고 알린다.
그래서 이걸 사용하기 위해서는
이렇게 중간 객체가 뭔지 한번 업케스팅을 해주고 그걸 또 업케스팅해야만 사용이 가능하다.
이를 해결하기 위해서는 중간에 상속을 분리해서 받는 ChildrenA와 ChildrenB 에서 상속하는 부분에서 virtual을 붙여주면
이때 부터는 이렇게 다중 상속이 되는 상황에서 최상위 부모인 Parent의 생성 권리를 다중상속하는 클래스에게 위임한다.
그래서 GrandChildren에서
최상위 부모인 Parent의 생성자를 호출할텐데 Parent에는 인자를 받는 생성자만 존재하기에
Parent클레스에 디폴트 생성자를 만들어주던가, 다중상속을 하는 GrandChildren에 인자를 받는 생성자를 직접 호출해주던가 해야한다.
이제 GrandChildren을 생성해보면
부모 생성자는 딱 한번만 GrandChildren에서 호출하는 것으로 생성되는 것을 알 수 있다.
그리고
이렇게 업케스팅도 따로 지정할 필요 없이 직접 해도 문제 없이 기능하게 된다.
이렇게 다중 상속시에 다이아몬드 상속과 이름이 겹치는 상황에서는 많이 고민하고 생각하고 구현해야만 한다.
가장 좋은 방식은 최대한 다중상속을 사용하지 않도록 노력하는게 제일 좋은 방식이라고 본다.