다중 상속
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 클래스는 이제 Printer와 Scanner 양쪽의 메서드에 접근할 수 있습니다:
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의 버전을 호출다중 상속은 강력하지만, 특히 기본 클래스들이 공통 조상을 공유할 때 복잡한 상황을 초래할 수 있습니다. 다음 레슨에서 그 문제를 살펴보겠습니다.
챌린지
쉬움다중 상속을 사용하여 여러 기본 클래스의 기능을 결합하는 스마트 홈 장치 시스템을 구축해 보겠습니다. WiFi에 연결할 수 있고 음성으로 제어할 수 있는 장치를 만든 다음, 이러한 기능들을 통합된 스마트 스피커로 결합할 것입니다.
코드는 세 개의 파일로 구성됩니다:
WiFiDevice.h: 네트워크에 연결할 수 있는 모든 장치를 나타내는WiFiDevice클래스를 정의합니다:- protected 멤버인
std::string networkName - 네트워크 이름을 매개변수로 받아 저장하는 생성자
Connecting to WiFi: <networkName>를 출력하는 publicconnect()메서드WiFi Status: Connected를 출력하는 publicstatus()메서드
- protected 멤버인
VoiceControl.h: 음성 기능을 가진 모든 장치를 나타내는VoiceControl클래스를 정의합니다:- protected 멤버인
std::string assistantName - 어시스턴트 이름을 매개변수로 받아 저장하는 생성자
<assistantName> is listening...을 출력하는 publiclisten()메서드Voice Status: Active를 출력하는 publicstatus()메서드
- protected 멤버인
main.cpp: 세 개의 입력을 읽습니다 (각각 별도의 줄에 입력됨):- 장치 이름 (string)
- WiFi 네트워크 이름 (string)
- 음성 어시스턴트 이름 (string)
WiFiDevice와VoiceControl을 모두 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: ActiveshowAllStatus() 메서드는 장치 이름 뒤에 " 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;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.