C++/Class

C++ 다이아몬드 상속 (Diamond Polymorphism)

Caniro 2021. 3. 21. 23:50

다이아몬드 상속 (Diamond Polymorphism)

  • 한 자식 클래스가 상속받는 서로 다른 부모 클래스들이, 같은 조부모 클래스를 상속받는 구조

예제

  • 클래스 AB, C가 상속받은 후 DBC를 상속받으면, D::B::AD::C::A가 각각 존재하게 된다.

    #include <iostream>
    
    class PoweredDevice
    {
    public:
      int    i_;
    
      PoweredDevice(const int& power)
      {
        std::cout << "PoweredDevice: " << power << '\n';
      }
    };
    
    class Scanner : public PoweredDevice
    {
    public:
      Scanner(const int& scanner, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Scanner: " << scanner << '\n';
      }
    };
    
    class Printer : public PoweredDevice
    {
    public:
      Printer(const int& printer, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Printer: " << printer << '\n';
      }
    };
    
    class Copier : public Scanner, public Printer
    {
    public:
      Copier(const int& scanner, const int& printer, const int& power)
        : Scanner(scanner, power), Printer(printer, power)
      {}
    };
    
    int            main()
    {
      using namespace std;
    
      Copier    cop(1, 2, 3);
      cout << &cop.Scanner::PoweredDevice::i_ << endl;
      cout << &cop.Printer::PoweredDevice::i_ << endl;
    }
    
    /* stdout
    PoweredDevice: 3
    Scanner: 1
    PoweredDevice: 3
    Printer: 2
    0000008F470FF798
    0000008F470FF79C
    */

  • 아래와 같이 상속 설정에서 virtual 키워드를 붙이면 해당 부모 클래스의 인스턴스가 하나만 사용된다.

    • 부모 클래스의 생성자를 따로 호출하지 않는 경우 에러가 발생했다.
    #include <iostream>
    
    class PoweredDevice
    {
    public:
      int    i_;
    
      PoweredDevice(const int& power)
      {
        std::cout << "PoweredDevice: " << power << '\n';
      }
    };
    
    class Scanner : virtual public PoweredDevice  // 여기서 virtual 키워드 사용
    {
    public:
      Scanner(const int& scanner, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Scanner: " << scanner << '\n';
      }
    };
    
    class Printer : virtual public PoweredDevice  // 여기서 virtual 키워드 사용
    {
    public:
      Printer(const int& printer, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Printer: " << printer << '\n';
      }
    };
    
    class Copier : public Scanner, public Printer
    {
    public:
      Copier(const int& scanner, const int& printer, const int& power)
        : Scanner(scanner, power), Printer(printer, power)  // 에러
      {}
    };
    
    int            main()
    {
      using namespace std;
    
      Copier    cop(1, 2, 3);
      cout << &cop.Scanner::PoweredDevice::i_ << endl;
      cout << &cop.Printer::PoweredDevice::i_ << endl;
    }
    error C2512: 'PoweredDevice::PoweredDevice': no appropriate default constructor available
    • 부모 클래스(PoweredDevice)의 생성자를 명시하지 않으면 기본 생성자가 호출되는데, 현재 인자가 없는 생성자가 존재하지 않으므로 에러가 발생한 것이다.
  • 에러가 나는 부분에 부모 클래스의 생성자를 호출하거나, 기본 생성자를 만들어주면 에러가 발생하지 않는다.

    #include <iostream>
    
    class PoweredDevice
    {
    public:
      int    i_;
    
      PoweredDevice(const int& power)
      {
        std::cout << "PoweredDevice: " << power << '\n';
      }
    };
    
    class Scanner : virtual public PoweredDevice
    {
    public:
      Scanner(const int& scanner, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Scanner: " << scanner << '\n';
      }
    };
    
    class Printer : virtual public PoweredDevice
    {
    public:
      Printer(const int& printer, const int& power)
        : PoweredDevice(power)
      {
        std::cout << "Printer: " << printer << '\n';
      }
    };
    
    class Copier : public Scanner, public Printer
    {
    public:
      Copier(const int& scanner, const int& printer, const int& power)
        : Scanner(scanner, power), Printer(printer, power), PoweredDevice(power)
      {}
    };
    
    int            main()
    {
      using namespace std;
    
      Copier    cop(1, 2, 3);
      cout << &cop.Scanner::PoweredDevice::i_ << endl;
      cout << &cop.Printer::PoweredDevice::i_ << endl;
    }
    
    /* stdout
    PoweredDevice: 3
    Scanner: 1
    Printer: 2
    00000094DE1BFA58
    00000094DE1BFA58
    */
    • 아까와 달리 PoweredDevice: 3이 한 번만 출력된 것을 볼 수 있고, 같은 메모리를 사용하는 것을 알 수 있다.