Menu
Coddy logo textTech

메서드 오버라이딩

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

메서드 오버라이딩(Method overriding)은 파생 클래스가 기본 클래스에 이미 존재하는 메서드에 대해 자체적인 구현을 제공할 때 발생합니다. 이를 통해 파생 클래스는 동일한 메서드 시그니처를 유지하면서 상속된 동작을 사용자 정의할 수 있습니다.

메서드를 오버라이드하려면, 파생 클래스에서 이름, 반환 타입, 매개변수가 완전히 동일한 메서드를 정의하기만 하면 됩니다:

class Animal {
public:
    void speak() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};

Dog d;
d.speak();  // 출력: Woof!

파생 클래스 버전은 기본 클래스 버전을 숨깁니다. 하지만 범위 지정 연산자를 사용하여 기본 클래스 메서드에 여전히 접근할 수 있습니다:

class Dog : public Animal {
public:
    void speak() {
        Animal::speak();  // 기본 클래스 버전을 호출합니다
        std::cout << "Woof!" << std::endl;
    }
};

이러한 기본적인 형태의 오버라이딩에는 중요한 제한 사항이 있습니다. 기초 클래스 포인터나 참조를 통해 파생 객체에 접근할 때, 대신 기초 클래스 버전이 호출됩니다:

Dog d;
Animal* ptr = &d;
ptr->speak();  // 출력: Some sound (Woof!가 아님)

이것은 컴파일러가 실제 객체 타입이 아니라 포인터 타입을 기반으로 호출할 메서드를 결정하기 때문에 발생합니다. 실제 객체를 기반으로 올바른 메서드가 호출되는 진정한 런타임 다형성을 구현하려면 가상 함수(virtual functions)가 필요하며, 이는 다음 레슨에서 다룰 예정입니다.

challenge icon

챌린지

쉬움

메서드 오버라이딩(method overriding)이 실제로 어떻게 작동하는지 보여주는 알림 시스템을 구축해 보겠습니다. 기본 Notifier 클래스와 메시지 전달 방식을 커스터마이징하는 두 개의 특화된 알림 클래스를 만들 것입니다.

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

  • Notifier.h: 다음을 포함하는 기본 Notifier 클래스를 정의합니다:
    • 보호된(protected) std::string recipient 멤버
    • 수신자 이름을 받아 저장하는 생성자
    • Sending to <recipient>: <message>를 출력하는 공개(public) send(const std::string& message) 메서드
  • EmailNotifier.h: Notifier를 공개 상속받는 EmailNotifier 클래스를 정의합니다:
    • 수신자를 받아 기본 클래스로 전달하는 생성자
    • send() 메서드를 오버라이드하여, 먼저 Notifier::send()를 사용하여 기본 클래스 버전을 호출한 다음, [Email sent successfully]를 출력합니다.
  • main.cpp: 두 개의 입력(각각 별도의 줄)을 읽습니다:
    1. 수신자 이름 (문자열)
    2. 메시지 내용 (문자열)

    main.cpp에 직접 Notifier를 공개 상속받는 SMSNotifier 클래스를 정의합니다:

    • 수신자를 받아 기본 클래스로 전달하는 생성자
    • send() 메서드를 오버라이드하여 SMS to <recipient>: <message>를 출력합니다 (기본 동작을 호출하지 않고 완전히 대체합니다).

    동일한 수신자를 사용하여 각 알림 유형(기본 Notifier, EmailNotifier, SMSNotifier)의 객체를 하나씩 생성합니다. 입력받은 메시지로 각각의 send()를 호출하고, 각 호출 사이에 구분선을 출력합니다:

    --- Base Notifier ---
    <output>
    --- Email Notifier ---
    <output>
    --- SMS Notifier ---
    <output>

예를 들어, 입력값이 AliceHello there!인 경우:

--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!

EmailNotifier는 부모 메서드를 먼저 호출하여 기본 동작을 확장하는 반면, SMSNotifier는 자체 구현으로 기본 동작을 완전히 대체하는 방식의 차이를 확인해 보세요. 헤더 가드(header guards)를 잊지 말고 각 파일에 적절한 헤더를 포함시키세요.

치트 시트

메서드 오버라이딩(Method overriding)을 사용하면 파생 클래스가 기본 클래스의 메서드에 대해 동일한 메서드 시그니처(이름, 반환 타입 및 매개변수)를 유지하면서 자체적인 구현을 제공할 수 있습니다.

기본적인 오버라이드 구문:

class Animal {
public:
    void speak() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};

Dog d;
d.speak();  // Output: Woof!

파생 클래스의 메서드는 기본 클래스의 버전을 숨깁니다. 파생 클래스에서 기본 클래스의 메서드를 호출하려면 범위 지정 연산자(scope resolution operator)를 사용하세요:

class Dog : public Animal {
public:
    void speak() {
        Animal::speak();  // 기본 클래스 버전 호출
        std::cout << "Woof!" << std::endl;
    }
};

중요한 제한 사항: 기본 클래스 포인터나 참조를 통해 파생 객체에 접근할 때, 기본 클래스의 버전이 호출됩니다:

Dog d;
Animal* ptr = &d;
ptr->speak();  // Output: Some sound (Woof!가 아님)

이는 컴파일러가 실제 객체 타입이 아닌 포인터 타입을 기반으로 호출할 메서드를 결정하기 때문입니다. 진정한 런타임 다형성을 위해서는 가상 함수(virtual functions)를 사용하세요.

직접 해보기

#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"

using namespace std;

// TODO: Notifier를 퍼블릭 상속받는 SMSNotifier 클래스를 여기에 정의하세요
// - recipient를 기본 클래스에 전달하는 생성자
// - send()를 오버라이드하여 SMS to <recipient>: <message>를 출력하세요
//   (기본 동작을 완전히 대체하고, 부모의 send()를 호출하지 마세요)

int main() {
    string recipient;
    string message;
    
    getline(cin, recipient);
    getline(cin, message);
    
    // TODO: recipient를 사용하여 Notifier 객체를 생성하세요
    // "--- Base Notifier ---"를 출력하세요
    // message와 함께 send()를 호출하세요
    
    // TODO: recipient를 사용하여 EmailNotifier 객체를 생성하세요
    // "--- Email Notifier ---"를 출력하세요
    // message와 함께 send()를 호출하세요
    
    // TODO: recipient를 사용하여 SMSNotifier 객체를 생성하세요
    // "--- SMS Notifier ---"를 출력하세요
    // message와 함께 send()를 호출하세요
    
    return 0;
}
quiz icon실력 점검

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

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