다중 상속

Jaemyeong Lee·2024년 11월 4일
0

FastCampusC++

목록 보기
73/78

코드에서 다중 상속의 개념과 virtual 키워드를 사용한 상속 문제 해결에 대해 설명합니다. 각 예제 케이스를 하나씩 분석해 보겠습니다.


case1: 기본 다중 상속

#pragma once

#include <iostream>

namespace case1
{
    class BaseA
    {
    public:
        int m = 0;
        void foo()
        {
            std::cout << "BaseA" << std::endl;
        }
    };

    class BaseB
    {
    public:
        int m = 0;
        void foo()
        {
            std::cout << "BaseB" << std::endl;
        }
    };

    class Derived : public BaseA, public BaseB 
    {
    };
}
  • #pragma once: 헤더 파일의 중복 포함 방지를 위한 전처리기 지시문입니다.
  • namespace case1: case1이라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.
  • class BaseA: 기본 클래스 BaseA입니다. int m이라는 멤버 변수가 있고, foo() 함수는 "BaseA"를 출력합니다.
  • class BaseB: 기본 클래스 BaseB입니다. int m이라는 멤버 변수가 있고, foo() 함수는 "BaseB"를 출력합니다.
  • class Derived : public BaseA, public BaseB: Derived 클래스는 BaseABaseB를 다중 상속합니다.

설명:

  • Derived 클래스는 BaseABaseB를 모두 상속받아 mfoo() 함수를 두 번 상속받습니다.
  • Derived 클래스에서 foo()를 호출할 때 BaseAfoo()를 호출할지 BaseBfoo()를 호출할지 모호합니다.
  • m 또한 두 개가 존재하므로, 어떤 m을 사용할지 명시해야 합니다.
  • 결론: 다중 상속에서 부모 클래스의 멤버가 동일할 때 모호성 문제가 발생합니다.

case2: 다중 상속으로 인한 중복 생성자 호출

#pragma once

#include <iostream>

namespace case2
{
    class Base
    {
    public:
        int m = 10;
        Base(int m) : m(m)
        {
            std::cout << "Base(" << m << ")" << std::endl;
        }
    };

    class BaseA : public Base
    {
    public:
        BaseA() : Base(10)
        {
            std::cout << "BaseA" << std::endl;
        }
    };

    class BaseB : public Base
    {
    public:
        BaseB() : Base(20)
        {
            std::cout << "BaseB" << std::endl;
        }
    };

    class Derived : public BaseA, public BaseB 
    {
    public:
        Derived()
        {
            std::cout << "Derived" << std::endl;
        }
    };
}
  • namespace case2: case2라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.
  • class Base: 기본 클래스 Base입니다. m이라는 멤버 변수를 초기화하는 생성자가 있습니다. 생성자에서 "Base(m)"을 출력합니다.
  • class BaseA : public Base: Base를 상속하는 BaseA 클래스입니다. BaseA의 생성자에서 Base(10)을 호출하고, "BaseA"를 출력합니다.
  • class BaseB : public Base: Base를 상속하는 BaseB 클래스입니다. BaseB의 생성자에서 Base(20)을 호출하고, "BaseB"를 출력합니다.
  • class Derived : public BaseA, public BaseB: Derived 클래스는 BaseABaseB를 다중 상속합니다. 생성자에서 "Derived"를 출력합니다.

설명:

  • Derived 객체를 생성할 때 Base 생성자가 두 번 호출됩니다. BaseABaseB가 각각 Base를 상속하기 때문입니다.
  • Base의 멤버 변수 m이 두 개 존재하며, 모호성 문제가 발생합니다.
  • 결론: 다중 상속으로 인해 동일한 기본 클래스가 두 번 생성되며, 중복 문제와 모호성 문제가 발생합니다.

case3: virtual 키워드를 사용한 가상 상속

#pragma once

#include <iostream>

namespace case3
{
    class Base
    {
    public:
        int m = 10;
        Base(int m) : m(m)
        {
            std::cout << "Base(" << m << ")" << std::endl;
        }
    };

    class BaseA : virtual public Base // virtual을 써서 Base를 한 개만 만든다
    {
    public:
        BaseA() : Base(10)
        {
            std::cout << "BaseA" << std::endl;
        }
    };

    class BaseB : virtual public Base // virtual을 써서 Base를 한 개만 만든다
    {
    public:
        BaseB() : Base(20)
        {
            std::cout << "BaseB" << std::endl;
        }
    };

    class Derived : public BaseA, public BaseB
    {
    public:
        Derived()
            : Base(30) // Base를 virtual 상속을 한다면 Base의 어떤 생성자를 호출할지 지정해줘야 한다
        {
            std::cout << "Derived" << std::endl;
        }
    };
}
  • namespace case3: case3라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.
  • class Base: 기본 클래스 Base입니다. m이라는 멤버 변수를 초기화하는 생성자가 있습니다. 생성자에서 "Base(m)"을 출력합니다.
  • class BaseA : virtual public Base: Base를 가상 상속하는 BaseA 클래스입니다. 생성자에서 "BaseA"를 출력합니다.
  • class BaseB : virtual public Base: Base를 가상 상속하는 BaseB 클래스입니다. 생성자에서 "BaseB"를 출력합니다.
  • class Derived : public BaseA, public BaseB: Derived 클래스는 BaseABaseB를 상속받습니다. Base의 가상 상속을 명시적으로 지정한 생성자를 호출합니다. 생성자에서 "Derived"를 출력합니다.

설명:

  • virtual 키워드를 사용하여 Base를 가상 상속하면, Derived 클래스에서 Base 객체가 한 번만 생성됩니다.
  • Derived 생성자에서 Base의 생성자를 명시적으로 호출해야 합니다. 이는 가상 상속 시 생성자 호출을 명확히 지정하는 역할을 합니다.
  • 결론: virtual 키워드를 사용해 다중 상속의 중복 생성 문제를 해결하고 모호성을 제거할 수 있습니다.

main 함수 분석

#include <iostream>
#include "case1.h"
#include "case2.h"
#include "case3.h"

using namespace std;

// 다중상속은 정말 필요하지 않으면 사용하지 말자
int main()
{
    case1::Derived d1;

    // 어떤 부모의 것을 호출해야할지 모호하다
    //d1.foo();
    //d1.m;

    d1.BaseA::foo();
    d1.BaseA::m;
    d1.BaseB::foo();
    d1.BaseB::m;

    cout << endl;
    case2::Derived d2; // Base 생성자 두 번 호출

    // BaseA 부모의 m인지 BaseB 부모의 m인지 모호하다
    //cout << d2.m;

    cout << d2.BaseA::m << endl;
    cout << d2.BaseB::m << endl;

    cout << endl;
    case3::Derived d3;

    // Base가 한 개만 만들어지기 때문에 모호하지 않다
    cout << d3.m << endl;
}
  • case1::Derived d1;: case1Derived 객체를 생성합니다. BaseABaseB의 모호성 문제로 인해 foo()m에 바로 접근할 수 없습니다.
  • d1.BaseA::foo();, d1.BaseB::foo();: 명시적으로 BaseABaseBfoo()를 호출합니다.
  • case2::Derived d2;: case2Derived 객체를 생성합니다. Base 생성자가 두 번 호출됩니다.
  • cout << d2.BaseA::m << endl;, cout << d2.BaseB::m << endl;: m에 접근할 때 모호성을 피하기 위해 BaseABaseB를 명시합니다.
  • case3::Derived d3;: case3Derived 객체를 생성합니다. Base 객체가 한 번만 생성됩니다.
  • cout << d3.m << endl;: m에 접근할 때 모호하지 않습니다. Base가 한 번만 생성되었기 때문입니다.

**결론

:**

  • 다중 상속은 모호성과 중복 생성 문제를 유발할 수 있습니다.
  • virtual 키워드를 사용한 가상 상속으로 이러한 문제를 해결할 수 있습니다.
  • 다중 상속은 복잡하고 관리하기 어렵기 때문에, 특별한 경우가 아니면 사용을 피하는 것이 좋습니다.
profile
李家네_공부방

0개의 댓글