..

Search

58) 멤버 함수 오버라이딩

58) 멤버 함수 오버라이딩

멤버 함수 오버라이딩


오버라이딩(overriding)

앞서 배운 함수 오버로딩(overloading)이란 서로 다른 시그니처를 갖는 여러 함수를 같은 이름으로 정의하는 것이었습니다.

함수 오버라이딩(overriding)이란 이미 정의된 함수를 무시하고, 같은 이름의 함수를 새롭게 정의하는 것이라고 할 수 있습니다.


멤버 함수 오버라이딩

C++에서 파생 클래스는 상속을 받을 때 명시한 접근 제어 권한에 맞는 기초 클래스의 모든 멤버를 상속받습니다.

이렇게 상속받은 멤버 함수는 그대로 사용해도 되고, 필요한 동작을 위해 재정의하여 사용할 수도 있습니다.

오버라이딩이란 멤버 함수의 동작만을 재정의하는 것이므로, 함수의 원형은 기존 멤버 함수의 원형과 같아야 합니다.

 

C++에서는 다음과 같은 방법을 통해 멤버 함수를 오버라이딩할 수 있습니다.

 

1. 파생 클래스에서 직접 오버라이딩하는 방법

2. 가상 함수를 이용해 오버라이딩하는 방법


파생 클래스에서의 오버라이딩

C++에서는 파생 클래스에서 상속받은 기초 클래스의 멤버 함수를 직접 재정의할 수 있습니다.

예제

void Person::ShowPersonInfo()

{

    cout << name_ << "의 나이는 " << age_ << "살입니다." << endl;

}

...

void Student::ShowPersonInfo()

{

    cout << "이 학생의 학번은 " << student_id_ << "입니다." << endl;

}

코딩연습 ▶

실행 결과

순신의 나이는 35살입니다.

이 학생의 학번은 123456789입니다.

 

위의 예제에서는 Person 클래스의 ShowPersonInfo() 멤버 함수를 Student 클래스에서 상속받아 재정의하고 있습니다.

그리고서 Student 객체에서 ShowPersonInfo() 멤버 함수를 호출하면 기초 클래스인 Person 클래스의 멤버 함수가 아닌 재정의한 멤버 함수가 호출됨을 알 수 있습니다.

 

또한, 범위 지정 연산자(::)를 사용하면 파생 클래스에서 기초 클래스의 원래 멤버 함수를 호출할 수도 있습니다.

예제

Student hong(123456789, "길동", 29);

hong.ShowPersonInfo();

hong.Person::ShowPersonInfo();

코딩연습 ▶

실행 결과

이 학생의 학번은 123456789입니다.

길동의 나이는 29살입니다.

 

위의 예제에서 Student 객체는 자신이 재정의한 ShowPersonInfo() 함수뿐만 아니라 기초 클래스의 원래 함수도 호출하고 있습니다.


파생 클래스에서 오버라이딩의 문제점

위와 같이 파생 클래스에서 멤버 함수를 직접 재정의하는 방법은 일반적인 상황에서는 잘 동작합니다.

하지만 이 방법은 다음 예제처럼 포인터 변수를 사용할 때, 예상치 못한 결과를 반환할 수도 있습니다.

예제

Person* ptr_person;

Person lee("순신", 35);

Student hong(123456789, "길동", 29);

 

ptr_person = &lee;

ptr_person->ShowPersonInfo();

ptr_person = &hong;

ptr_person->ShowPersonInfo();

코딩연습 ▶

실행 결과

순신의 나이는 35살입니다.

길동의 나이는 29살입니다.

 

위의 예제는 먼저 Person 객체를 가리킬 수 있는 포인터 변수 ptr_person을 생성하여, Person 객체의 주소값을 대입합니다.

그 후 포인터 변수에 멤버 접근 연산자(->)를 사용하여 Person 객체의 ShowPersonInfo() 함수를 호출합니다.

그 다음에 Student 객체의 주소값을 대입하고, 이번에는 Student 객체의 ShowPersonInfo() 함수를 호출합니다.

 

하지만 결과는 두 번 모두 Person 객체의 ShowPersonInfo() 함수가 호출됩니다.

왜냐하면, C++ 컴파일러는 포인터 변수가 실제로 가리키는 객체의 타입을 기준으로 함수를 호출하는 것이 아니라, 해당 포인터의 타입을 기준으로 함수를 호출하기 때문입니다.

따라서 Person 객체를 가리킬 수 있는 포인터 변수로는 Person 객체의 멤버 함수만을 호출할 수 있습니다.

 

이러한 문제점을 해결하기 위해서 C++에서는 virtual 키워드를 사용한 가상 함수를 제공하고 있습니다.

예제

class Student : public Person

{

private:

    int student_id_;

public:

    Student(int sid, const string& name, int age); // 파생 클래스 생성자의 선언

    virtual void ShowPersonInfo(); // 파생 클래스에서 상속받은 멤버 함수의 재정의

};

코딩연습 ▶

실행 결과

순신의 나이는 35살입니다.

이 학생의 학번은 123456789입니다.

 

위의 예제는 앞선 예제에서 사용한 ShowPersonInfo() 함수를 가상 함수로 선언한 예제입니다.

이렇게 멤버 함수를 가상 함수로 선언하면 포인터가 실제로 가리키는 객체에 따라 호출하는 대상을 바꿀 수 있게 됩니다.

 

가상 함수를 이용한 멤버 함수의 오버라이딩에 대한 더 자세한 사항은 C++ 가상 함수 수업에서 확인할 수 있습니다.

 

C++ 가상 함수 수업 확인 =>


연습문제