Menu
Coddy logo textTech

다중 상속

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 53번째.

C++에서는 클래스가 여러 개의 기본 클래스로부터 동시에 상속받는 것을 허용합니다. 이것을 다중 상속이라고 하며, 서로 다른 클래스 계층 구조의 기능을 결합할 수 있게 해줍니다.

여러 클래스로부터 상속받으려면, 쉼표로 구분하여 나열합니다:

class Printer {
public:
    void print() {
        std::cout << "Printing document" << std::endl;
    }
};

class Scanner {
public:
    void scan() {
        std::cout << "Scanning document" << std::endl;
    }
};

class MultiFunctionDevice : public Printer, public Scanner {
public:
    void copy() {
        scan();
        print();
    }
};

MultiFunctionDevice 클래스는 이제 PrinterScanner 양쪽의 메서드에 접근할 수 있습니다:

MultiFunctionDevice mfd;
mfd.print();  // Printer에서 제공
mfd.scan();   // Scanner에서 제공
mfd.copy();   // 자체 메서드

생성자는 상속 선언에 기본 클래스가 나열된 순서(왼쪽에서 오른쪽)대로 호출되며, 소멸자는 그 반대 순서로 호출됩니다. 초기화 리스트에서 각 기본 생성자에 인수를 전달할 수 있습니다:

class MultiFunctionDevice : public Printer, public Scanner {
public:
    MultiFunctionDevice() : Printer(), Scanner() {}
};

두 기본 클래스 모두에 이름이 같은 멤버가 있을 때 잠재적인 문제가 발생합니다. 이는 모호성을 유발하며, 어떤 멤버를 의미하는지 지정하기 위해 범위 지정 연산자를 사용해야 합니다:

class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};

C obj;
obj.A::show();  // A의 버전을 호출
obj.B::show();  // B의 버전을 호출

다중 상속은 강력하지만, 특히 기본 클래스들이 공통 조상을 공유할 때 복잡한 상황을 초래할 수 있습니다. 다음 레슨에서 그 문제를 살펴보겠습니다.

challenge icon

챌린지

쉬움

다중 상속을 사용하여 여러 기본 클래스의 기능을 결합하는 스마트 홈 장치 시스템을 구축해 보겠습니다. WiFi에 연결할 수 있고 음성으로 제어할 수 있는 장치를 만든 다음, 이러한 기능들을 통합된 스마트 스피커로 결합할 것입니다.

코드는 세 개의 파일로 구성됩니다:

  • WiFiDevice.h: 네트워크에 연결할 수 있는 모든 장치를 나타내는 WiFiDevice 클래스를 정의합니다:
    • protected 멤버인 std::string networkName
    • 네트워크 이름을 매개변수로 받아 저장하는 생성자
    • Connecting to WiFi: <networkName>를 출력하는 public connect() 메서드
    • WiFi Status: Connected를 출력하는 public status() 메서드
  • VoiceControl.h: 음성 기능을 가진 모든 장치를 나타내는 VoiceControl 클래스를 정의합니다:
    • protected 멤버인 std::string assistantName
    • 어시스턴트 이름을 매개변수로 받아 저장하는 생성자
    • <assistantName> is listening...을 출력하는 public listen() 메서드
    • Voice Status: Active를 출력하는 public status() 메서드
  • main.cpp: 세 개의 입력을 읽습니다 (각각 별도의 줄에 입력됨):
    1. 장치 이름 (string)
    2. WiFi 네트워크 이름 (string)
    3. 음성 어시스턴트 이름 (string)

    WiFiDeviceVoiceControl을 모두 public 상속받는 SmartSpeaker 클래스를 정의합니다:

    • private 멤버인 std::string deviceName
    • 세 개의 매개변수를 모두 받고, 초기화 리스트를 사용하여 네트워크 이름을 WiFiDevice에, 어시스턴트 이름을 VoiceControl에 전달하며 장치 이름을 저장하는 생성자
    • <deviceName> powering on...을 출력한 다음, connect()listen()을 호출하는 powerOn() 메서드
    • 장치 이름을 출력한 다음, 범위 지정 연산자(scope resolution operator)를 사용하여 두 status() 메서드를 모두 호출함으로써 모호성을 해결하는 showAllStatus() 메서드

    입력값으로 SmartSpeaker 객체를 생성합니다. powerOn()을 호출하고, 빈 줄을 출력한 다음, showAllStatus()를 호출합니다.

예를 들어, 입력값이 Echo, HomeNetwork, Alexa인 경우:

Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...

Echo Status:
WiFi Status: Connected
Voice Status: Active

showAllStatus() 메서드는 장치 이름 뒤에 " Status:"를 붙여 한 줄에 출력해야 합니다. 그 다음, 두 기본 클래스 모두 동일한 이름의 메서드를 가지고 있으므로 모호성을 피하기 위해 WiFiDevice::status()VoiceControl::status()를 사용하여 각 기본 클래스의 status() 메서드를 호출해야 합니다.

치트 시트

C++는 다중 상속(multiple inheritance)을 지원하여, 하나의 클래스가 여러 개의 기본 클래스로부터 동시에 상속받을 수 있도록 합니다:

class DerivedClass : public BaseClass1, public BaseClass2 {
    // Class body
};

파생 클래스는 모든 기본 클래스의 멤버에 접근할 수 있습니다:

class Printer {
public:
    void print() {
        std::cout << "Printing document" << std::endl;
    }
};

class Scanner {
public:
    void scan() {
        std::cout << "Scanning document" << std::endl;
    }
};

class MultiFunctionDevice : public Printer, public Scanner {
public:
    void copy() {
        scan();   // From Scanner
        print();  // From Printer
    }
};

기본 클래스 생성자는 상속 목록에 나타나는 순서대로(왼쪽에서 오른쪽으로) 호출됩니다. 각 기본 생성자에 인수를 전달하려면 초기화 목록을 사용하세요:

class MultiFunctionDevice : public Printer, public Scanner {
public:
    MultiFunctionDevice() : Printer(), Scanner() {}
};

여러 기본 클래스에 이름이 같은 멤버가 있는 경우, 범위 지정 연산자(scope resolution operator)를 사용하여 모호성을 해결합니다:

class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};

C obj;
obj.A::show();  // Call A's version
obj.B::show();  // Call B's version

직접 해보기

#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"

using namespace std;

// TODO: WiFiDevice와 VoiceControl을 모두 퍼블릭 상속받는 SmartSpeaker 클래스를 정의하세요
// - 프라이빗 멤버: deviceName (string)
// - 생성자: deviceName, networkName, assistantName을 매개변수로 받음
//   초기화 리스트를 사용하여 networkName을 WiFiDevice에, assistantName을 VoiceControl에 전달하세요
// - powerOn() 메서드: "<deviceName> powering on..."을 출력한 후, connect()와 listen()을 호출함
// - showAllStatus() 메서드: "<deviceName> Status:"를 출력한 후, 두 status() 메서드를 모두 호출함
//   범위 지정 연산자(WiFiDevice::status() 및 VoiceControl::status())를 사용하세요

class SmartSpeaker {
    // TODO: 여기에 SmartSpeaker 클래스를 구현하세요
};

int main() {
    string deviceName, networkName, assistantName;
    getline(cin, deviceName);
    getline(cin, networkName);
    getline(cin, assistantName);

    // TODO: 입력받은 값들로 SmartSpeaker 객체를 생성하세요
    // TODO: powerOn()을 호출하세요
    // TODO: 빈 줄을 출력하세요
    // TODO: showAllStatus()를 호출하세요

    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨