오버라이딩(Overriding)과 정적 바인딩(Static Binding)
override : 우선하다
overload: 다중 정의
overwrite : 덮어 쓰다
Overriding
#include <iostream>
using namespace std;
class Base {
public:
int a = 10; // Base() : a(10) {}이랑 똑같음.
void Print() {
cout << "From Base!!!" << endl;
}
};
int a = 10;
- 생성자가 호출될 때, 멤버 변수 a를 10으로 초기화 된다
class Derived : public Base {
public:
int a = 20;
void Print() {
cout << "From Derived!!!" << endl;
}
};
int main(void) {
Base b;
Derived d;
cout << b.a << endl; // result : 10
cout << d.a << endl; // result : 20, 자기 고유멤버가 상속멤버보다 우선순위가 높음.
cout << d.Base::a << endl; // result : 10 : d의 멤버중에 Base에 속해있는 a를 출력해
cout << d.Derived::a << endl; // result : 20
d.Print();
d.Base::Print(); // Base 명시
d.Derived::Print();
}
멤버 변수의 이름이 같다면, 자기 고유멤버가 상속멤버보다 우선순위가 높다.
- d에서 멤버변수 a에 접근할 때, 부모의 a가 아닌 자신의 a로 접근한다(멤버 변수 오버라이딩)
부모의 a에 접근하려면
소속 클래스를 명시해주면 된다
d.Base::a
멤버 메서드도 멤버 변수처럼 오버라이딩이 가능하다
정적 바인딩(Static Binding)
Derived d;
Base *b = &d; // 에러 X. 부모클래스의 포인터가 자식클래스를 가리킬 수 있다.
부모클래스의 포인터가 자식클래스를 가리킬 수 있다.
Base : 남자, Derived : 총각. 남자-> 남자, 남자 -> 총각 문제가 없다. 남자)총각이니까.
int main() {
Base *b = new Derived();
b->Print(); // result : From Base!! Why?
delete b;
}
실행 결과:
result : From Base
부모의 Print() 메서드가 호출되었다.
- Why?
Print() 멤버함수가 오버라이딩 되어있지 않은 Derived2라는 클래스가 있다고 해보자
class Derived2 : public Base {
// print 함수 오버라이딩 X
};
int main() {
Base *b = new Derived();
b->Print();
b = new Derived2(); // 만약 derived의 Print를 호출하게했다고치면, b가 derived2를 가리키게 됬을 때 아래 코드에서 에러를 호출하거나, 갑자기 부모의 Print를 호출해야함. --> 일관성이 없고 모호해짐.
b->Print(); // 그래서 부모의 함수를 출력한다. --> 어떤걸 호출해야하는지를 바인딩이라고 한다.
// 정적바인딩은 컴파일시간에 b->print가 Base.print 가되는걸, 정적 바인딩.
// b가 가리키는 객체가 부모인지, 자식인지 구별하기 힘드니, 부모클래스를 호출하라는게 정적 바인딩.
// 동적바인딩은 b가 Base가 될수도, derived가 될수도 derived2가 될수도 있게 하는 걸 동적 바인딩.
delete b;
}
b->Print()에서 부모의 Print()가 아니라, 자식의 Print()를 호출하게 했다고 가정해보자.
만약 b가 또 다른 자식인 Derived2() 객체를 가리키게 되었다면, 컴파일러는 자식에 Print()를 호출할 수 없으므로 에러를 발생시키거나, 부모의 Print()를 호출해야 한다.
- 이 경우 부모의 Print()를 호출한다면 일관성이 없어진다.
- 그리고 코드가 길어지면 b가 부모 객체를 가리키는지, Derived 객체를 가리키는지, Derived2 객체를 가리키는지를 확인하는게 힘들어진다
그래서 b의 타입인 Base의 멤버 함수 Print()를 실행하도록 정한 것이다
C++은 아무 것도 하지 않으면 기본적으로 정적 바인딩이 일어나게 된다.
현재 가리키는 객체의 타입이 무엇인지 확인하지 않고, 자신의 타입, 즉 부모 클래스의 멤버 함수를 호출한다.
Java 등 다른 기타 언어와는 다르게 C++에서만 디폴트로 정적 바인딩이 발생한다
CF) Java: non-static 클래스에서 기본적으로 동작 바인딩을 사용한다 (@Override)
동적 바인딩은 b->print()가 Base의 print()가 될수도, derived의 print()가 될수도 derived2의 print()가 될 수도 있게 하는 걸 동적 바인딩.
C++에서 동적 바인딩을 구현하기 위해서는 가상 함수라는 것을 사용해야 한다. 다음 게시글에서 다룰 예정
참조) 두들낙서 C/C++ 강좌
'Programming Language > C++' 카테고리의 다른 글
[C++] 상속이 필요한 이유(2) (0) | 2024.06.27 |
---|---|
[C++] 가상 함수(Virtual Function)와 동적 바인딩(Dynamic Binding) (0) | 2024.06.27 |
[C++] 상속이 필요한 이유(1) (0) | 2024.06.27 |
[C++] 상속(Inheritance)과 접근제어(Access Control) (0) | 2024.06.27 |
[C++] Implicit Conversion(묵시적 형변환) (0) | 2024.06.27 |