Dynamic Binding (동적 바인딩)

그런데, 우리가 두 클래스가 상속관계에 있다는 것을 안다면, 이 멤버함수를 자동으로 묶어줄 수는 없을까?

이제 override , virtual 의 강력한 기능을 알 수 있다.

#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
class Base{
public:
    void f(){cout << "Base::f()" << endl;}
    virtual void vf() {cout << "Base::vf()" << endl;}
};
 
class Derived:public Base{
public:
    void f(){ cout << "Derived::f()" << endl;}
    void vf() override { cout << "Derived::vf()" << endl;}
};
 
int main(){
    
    Base base;
    Derived derived;
    Base* pBase;
    
    pBase = &base;
    pBase->f();
    pBase->vf();
    
    pBase = &derived;
    pBase->f();
    pBase->vf();
    
    return 0;
}
Base::f()
Base::vf()
Base::f()
Derived::vf()
Program ended with exit code: 0

Base 클래스의 주소를 담는 포인터 변수 pBase 를 선언하고, 이 주소에 base 객체의 주소를 담고서, 두 멤버함수를 호출하니 당연히 Base 클래스의 함수들이 호출되는 것을 알 수 있다.

그런데, derived 객체의 주소를 담으면 (부모 클래스에 자식 클래스를 넣을 수는 있다고 앞글에서 설명했다.) 멤버함수의 Base의 f(), Derived 의 vf() 가 호출되었음을 알 수 있다.

즉, 해당 객체의 멤버함수를 기본적으로는 Base 에서 가져온다.

하지만 Virtual 선언이 되어 있다면, 어떤 객체인지 파악후 그 객체의 멤버함수를 가져온다

이런 작업은, 프로그램이 실행되면서 묶일 수 밖에 없으므로, 동적 바인딩 이라 불린다.

동적 바인딩의 의미

우리는 이 기술을 사용해서, 서로 다른 자료형들을 하나의 벡터안에 넣어서 관리할 수 있다.

#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
class Base{
public:
    void f(){cout << "Base::f()" << endl;}
    virtual void vf() {cout << "Base::vf()" << endl;}
};
 
class Derived:public Base{
public:
    void f(){ cout << "Derived::f()" << endl;}
    void vf() override { cout << "Derived::vf()" << endl;}
};
 
int main(){
 
    Base* pBase;
    vector<Base*> v{ new Base, new Derived, new Base};
    pBase = new Derived;
    v.push_back(pBase);
    pBase = new Base;
    v.push_back(pBase);
    
    for (auto elem: v)
        elem->vf();
 
    return 0;
}
 
Base::vf()
Derived::vf()
Base::vf()
Derived::vf()
Base::vf()
Program ended with exit code: 0

Base 클래스의 주소를 담는 자료형을 기반으로 벡터를 만들고, 그 안에, 내가 원하는 클래스를 담으면, virtual override 구조에 따라서 자동으로 멤버함수가 결정된다!

Pure virtual function (순수 가상 함수)

virtual 을 선언할 때, 특별히 기본 Base 클래스에서는 기능을 정의하지 않고 파생 클래스에서 이 함수를 정의해서 사용할 때가 있는데 이때 선언하는 것이 순수 가상함수 이다.

virtual print() = 0;

순수 가상함수를 선언할 때는, base 클래스에서 이 함수의 작동이 없다는 것을 명시하기 위해서 뒤에 =0 을 추가로 달아준다.

이 표시가 있을 경우 우리는 파생 클래스로부터 이 함수를 필수적으로 정의해야한다.

또한 Base 클래스에서 위와 같이 선언했을 경우 main 함수에서 우리는 저 함수를 사용할 수 없다.

abstract Class(추상 클래스)

내가 파생클래스들로 무언가를 만들어 사용하고 싶을 때, 그 윗단계에서 이 함수들에 대한 개략적인 것들을 적어둔 상위 집합의 클래스를 만들 수 있을 것이다.

이 때, 순수 가상함수들로 구성된 하나의 클래스를 추상클래스 라 부른다. 이렇게 관리할 경우 우리는 어떤 클래스들의 공통된 특징을 묶어서 관리할 수 있으므로 용이하다.