Programming Language/C++

[C++] 오버라이딩(Overriding)과 정적 바인딩(Static Binding)

lumana 2024. 6. 27. 18:52

오버라이딩(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++ 강좌