가상 함수 다시 보기
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 58번째.
이제 컴파일 시간 다형성과 런타임 다형성의 차이를 이해했으므로, 가상 함수와 이를 더 안전하게 사용할 수 있도록 해 주는 override 키워드를 더 자세히 살펴보겠습니다.
virtual로 표시한 function을 base 클래스에서 정의하면 파생 클래스가 자체 구현을 제공할 수 있습니다. override 지정자는 virtual function을 재정의하려는 의도를 컴파일러에 명시적으로 알립니다.
class Animal {
public:
virtual void speak() {
std::cout << "Some sound" << std::endl;
}
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof!" << std::endl;
}
};override를 사용하는 것은 컴파일 시점에 오류를 잡아내기 때문에 중요합니다. 함수 이름을 실수로 잘못 입력하거나 잘못된 매개변수를 사용하면, 컴파일러가 조용히 새 function을 만드는 대신 경고를 표시합니다.
class Cat : public Animal {
public:
void speek() override { // 컴파일러 오류: 오버라이드할 함수가 없음
std::cout << "Meow!" << std::endl;
}
};final 지정자는 더 이상의 오버라이드를 방지합니다. 파생 클래스가 특정 function을 오버라이드하는 마지막 클래스여야 할 때 사용하세요.
class Bulldog : public Dog {
public:
void speak() override final {
std::cout << "Gruff woof!" << std::endl;
}
};
class TinyBulldog : public Bulldog {
void speak() override {} // 오류: final 함수를 오버라이드할 수 없음
};가상 함수를 재정의할 때는 항상 override를 사용하세요. 이는 의도를 문서화하고, 실제로 기존 가상 함수를 재정의하고 있는지 컴파일러가 확인할 수 있도록 합니다.
챌린지
쉬움override와 final 키워드를 가상 함수와 함께 사용하여 그 강력한 기능을 보여 주는 알림 시스템을 만들어 봅시다. 일부 메서드는 추가로 사용자 지정할 수 있고, 다른 메서드는 수정을 방지하기 위해 잠기는 알림 처리기 계층 구조를 만들게 됩니다.
코드를 세 개의 파일로 구성합니다:
Notifier.h: 모든 알림 발신자를 나타내는 기본Notifierclass를 정의합니다:- protected
std::string recipient멤버 - 수신자 이름을 받아 저장하는 constructor
- 다음을 출력하는 virtual
send(const std::string& message)메서드:Notifying <recipient>: <message> "Generic"문자열을 반환하는 virtualgetType()메서드- virtual destructor
- protected
EmailNotifier.h:Notifier에서 상속하는EmailNotifierclass를 정의합니다:- private
std::string domain멤버 - recipient와 domain을 받아 recipient를 base class에 전달하는 constructor
- 다음을 출력하도록
send()를 override합니다:Emailing <recipient>@<domain>: <message> getType()을 override하고final로 표시합니다."Email"을 반환해야 합니다.
그런 다음
EmailNotifier에서 상속하는UrgentEmailNotifierclass를 정의합니다:- recipient와 domain을 받아 둘 다
EmailNotifier에 전달하는 constructor - 다음을 출력하도록
send()를 override합니다:[URGENT] Emailing <recipient>@<domain>: <message> - 참고:
EmailNotifier에서final로 표시되었기 때문에 여기에서는getType()을 override할 수 없습니다.
- private
main.cpp: 세 개의 입력을 읽습니다(각각 별도의 줄):- Recipient 이름
- Email domain
- Message 텍스트
세 개의 notifier 객체를 동적으로 생성합니다: base
Notifier,EmailNotifier,UrgentEmailNotifier각각 하나씩 생성하며, 모두 동일한 recipient(해당하는 경우 domain도)를 사용합니다. 이들을Notifier*포인터의 array에 저장합니다.array를 순회하면서 각 notifier에 대해
getType()을 사용하여 type을 출력한 다음, message와 함께send()를 호출합니다. 각 항목을 다음 형식으로 지정합니다:Type: <type> <send output>각 notifier 사이에 blank line을 출력합니다. 작업이 끝나면 동적으로 할당한 객체를 정리합니다.
예를 들어 입력이 Alice, company.com, Meeting at 3pm인 경우:
Type: Generic
Notifying Alice: Meeting at 3pm
Type: Email
Emailing Alice@company.com: Meeting at 3pm
Type: Email
[URGENT] Emailing Alice@company.com: Meeting at 3pmUrgentEmailNotifier는 send()를 override하여 message 형식을 사용자 지정할 수 있지만, getType()이 final로 표시되었기 때문에 EmailNotifier에서 "Email" type을 상속한다는 점에 주목하세요. 컴파일 시 signature 불일치를 확인하려면 모든 override된 메서드에 override 키워드를 사용하세요.
직접 해보기
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
int main() {
// 입력 읽기
string recipient;
string domain;
string message;
getline(cin, recipient);
getline(cin, domain);
getline(cin, message);
// TODO: Notifier* 포인터 배열을 요소 3개로 생성
// TODO: 동적으로 생성:
// - recipient를 가진 기본 Notifier
// - recipient와 domain을 가진 EmailNotifier
// - recipient와 domain을 가진 UrgentEmailNotifier
// TODO: 배열을 순회하며 각 notifier에 대해:
// - "Type: " 다음에 getType() 결과를 출력
// - message로 send() 호출
// - notifier 사이에 빈 줄 출력 (마지막 뒤에는 출력하지 않음)
// TODO: 동적으로 할당된 객체 정리
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 C++ 컴파일러