오버라이딩 (overriding)
파생 클래스는 상속을 받을 때 명시한 접근 제어 권한에 맞는 기초 클래스의 모든 멤버를 상속받는다.
이렇게 상속받은 멤버 함수는 그대로 사용해도 되고, 재정의하여 사용할 수도 있다.
멤버 함수의 동작만을 재정의 하는 것이므로, 함수의 원형은 기존 멤버 함수의 원형과 같아야 한다.
오버로딩과 오버라이딩
- 오버로딩 : 새로운 메서드를 정의하는 것
- 오버라이딩 : 상속받은 기존의 메서드를 재정의 하는 것
오버라이딩 조건
- 파생 클래스에서 직접 오버라이딩
- 가상 함수를 이용해 오버라이딩
파생 클래스에서 오버라이딩
클래스의 상속
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base() : s("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
class Derived : public Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
what();
}
};
int main()
{
std::cout << " === 기반 클래스 생성 ===" << std::endl;
Base p;
std::cout << " === 파생 클래스 생성 ===" << std::endl;
Derived c;
return 0;
}
실행 결과
21행의 what() 함수를 호출하는 부분을 보면, Derived 클래스에서 정의되어 있지 않은 what() 함수를 호출이 가능하다.
이유는 Base의 모든 정보를 상속받았기 때문에 Derived에서도 what()을 호출할 수 있게 된다.
또한, what() 함수는 Base에 정의가 되어있기 때문에 Derived의 s인 '파생'이 아니라 Base의 s가 출력되어 '기반'이 출력된다.
what() 함수의 오버라이딩
Derived에도 what() 함수를 정의해 보자.
#include <iostream>
#include <string>
class Base
{
std::string s;
public:
Base() : s("기반") { std::cout << "기반 클래스" << std::endl; }
void what() { std::cout << s << std::endl; }
};
class Derived : public Base
{
std::string s;
public:
Derived() : Base(), s("파생")
{
std::cout << "파생 클래스" << std::endl;
what();
}
void what() { std::cout << s << std::endl; }
};
int main()
{
std::cout << " === 기반 클래스 생성 ===" << std::endl;
Base p;
std::cout << " === 파생 클래스 생성 ===" << std::endl;
Derived c;
return 0;
}
실행 결과
Derived 클래스 안에 what() 함수를 추가해 주면 '파생'이 출력된다.
Derived의 생성자에서 what을 호출할 때 굳이 Base 클래스의 함수들까지 찾지 않고 바로 앞에 있는 Derived의 what() 함수를 호출하게 된다.
즉, Derived의 what() 함수가 Base의 what() 함수를 오버라이딩 했다고 말할 수 있다.
가상 함수를 이용해 오버라이딩
가상 함수는 기반 클래스에서 정의된 함수를 파생 클래스에서 재정의할 수 있도록 하는 함수이다.
가상 함수를 선언할 때, 함수 앞에 virtual 키워드를 붙이고 파생 클래스에서 재정의 할 때에는 함수 앞에 override를 붙인다.
class Shape {
public:
virtual void draw() {
std::cout << "Shape의 draw() 함수 호출" << std::endl;
}
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Circle의 draw() 함수 호출" << std::endl;
}
};
class Square : public Shape {
public:
void draw() override {
std::cout << "Square의 draw() 함수 호출" << std::endl;
}
};
int main() {
Shape* shapes[2];
shapes[0] = new Circle();
shapes[1] = new Square();
for (int i = 0; i < 2; ++i) {
shapes[i]->draw();
}
delete shapes[0];
delete shapes[1];
return 0;
}
draw() 함수를 가상 함수로 선언
파생 클래스에서 draw() 함수를 재정의 할 수 있게 한다.
class Shape {
public:
virtual void draw() {
std::cout << "Shape의 draw() 함수 호출" << std::endl;
}
};
파생 클래스에서 기반 클래스의 함수를 재정의
override 키워드를 사용하여 기반 클래스 함수를 재정의한다.
class Circle : public Shape {
public:
void draw() override {
std::cout << "Circle의 draw() 함수 호출" << std::endl;
}
};
draw() 함수 호출
Shape 클래스를 상속받은 클래스들이 draw() 함수를 다르게 구현하고 있어도 같은 코드를 사용하여 객체의 draw() 함수를 호출할 수 있다.
int main() {
Shape* shapes[2];
shapes[0] = new Circle();
shapes[1] = new Square();
for (int i = 0; i < 2; ++i) {
shapes[i]->draw();
}
delete shapes[0];
delete shapes[1];
return 0;
}
위 코드를 실행해보면, 각각의 클래스에서 재정의한 draw() 함수가 호출된다