(수정필요)[C++] 가상함수와 추상클래스

    함수의 단순 재정의
    #include <iostream>
    using namespace std;
    
    class Base {
    public:
    	void f() { cout << "Base :: f() called" << endl; }
    };
    
    class Derived : public Base{
    public:
    	void f() { cout << "Derived :: f() callled" << endl; }
    };
    
    void main() {
    	Derived d, * pDer;
    	pDer = &d;
    	pDer->f();
    
    	Base* pBase;
    	pBase = pDer;
    	pBase->f();
    }

    이 코드는 기본클래스의 함수를 파생클래스에서 단순 재정의 한 것으로, 단순히 컴파일 시간에 결정된 함수가 호출되는 정적 바인딩에 속한다. 실행결과는 아래와 같다.

    Derived :: f() callled
    Base :: f() called

     

    함수의 오버라이딩
    #include <iostream>
    using namespace std;
    
    class Base {
    public:
    	virtual void f() { cout << "Base :: f() called" << endl; }
    };
    
    class Derived : public Base {
    public:
    	virtual void f() { cout << "Derived :: f() callled" << endl; }
    };
    
    void main() {
    	Derived d, * pDer;
    	pDer = &d;
    	pDer->f();
    
    	Base* pBase;
    	pBase = pDer;
    	pBase->f();
    }

    이번에는 기본클래스(Base Class)와 파생클래스(Derived Class)에서 함수를 virtual로 정의했다.

    다시 말하자면, 함수를 overriding 한 것이다. virtual 키워드를 이용해 함수를 overriding 한 경우, 파생클래스로 부터 생성된 객체에서 기본클래스의 함수는 존재감을 상실하고 무시된다. 이 코드의 실행결과는 다음과 같다.

    Derived :: f() callled
    Derived :: f() callled

     

    두 코드의 실행결과를 비교하면, 단순 함수 재정의를 하는 경우 정적바인딩이 일어나, 업캐스팅한 기본클래스의 포인터에서 기본클래스의 함수가 호출되는 반면, virtual 키워드를 이용하여 overriding 하는 경우 기본클래스의 함수가 존재감을 상실하여 파생클래스에서 재정의 된 함수가 호출되는 동적바인딩이 일어난다.

     

    동적바인딩과 정적바인딩

    앞단에서 계속해서 동적바인딩과 정적바인딩을 언급했는데, 기본적인 정의를 이해하고 가자.

    우선 바인딩이란 무엇인가. 간단히 말하자면 바인딩은 프로그램의 구성요소를 확정짓는 것을 의미한다. 

    일반적으로 개발자가 프로그램을 작성해서 컴파일하면, 더 이상 값을 변경할 수 없는 상태가 된다. 이것이 바로 바인딩이고

    메모리에 값을 할당하는 것 또한 바인딩이다.

     

    정적바인딩과 동적바인딩의 차이는 바인딩 되는 시점에 따라 분류한 것인데, 정적바인딩의 경우 컴파일 시 값이 정해지고, 그 후로는 불변하며, 동적바인딩의 경우 실행시간(런타임)에 값이 확정되어, 실행시간 전까지 값이 바뀔 수 있다,

     

    대개 정적바인딩의 경우, 값이 확정돼있기 때문에 안정적이고 실행만 시키면 되므로 빠르고 효율적이다.

    반면, 동적바인딩의 경우 실행시간까지 값이 바뀔 수 있기 때문에, 유연하지만, 들어올 값보다 메모리를 많이 차지하기 때문에 메모리 사용이 비교적 크고 실행시간이 길어진다는 단점이 있다.

     

    오버라이딩과 오버로딩

    객체 지향 언어를 공부하다 보면, 비슷한 두 단어 때문에 혼동이 올 때가 있다. 오버라이딩과 오버로딩의 차이를 알아보자.

    우선 오버로딩이란, 함수명과 리턴타입이 같은 함수를 여러개 정의하는 것으로, 매개변수 타입과 갯수에 차이를 두어, 같은 이름의 함수를 여러 방면에서 사용할 수 있도록 한 것이다.

    다음으로 오버라이딩이란, 파생클래스에서 기본클래스의 함수를 재정의(overriding) 하는 것으로 virtual과 같은 지시어를 사용하여 기본클래스의 함수를 숨기고 새로 정의하는 것을 의미한다. 

     

    범위지정 연산자의 존재

    그렇다면, 파생클래스에서 overriding 한 내용을 무시하는 방법은 없을까? 

    범위지정 연산자가 그에 대한 답이 될 수 있다. 범위지정연산자 "::" 는 함수를 overriding 시 정적바인딩을 지시하는 연산자이다. 기본클래스에서 virtual 지시어로 정의된 함수를 파생클래스에서 overriding 할 시, 기본클래스 :: 가상함수()의 형태로 기본클래스의 함수를 정적바인딩으로 호출할 수 있다. 이렇게 되면 기본클래스의 함수에 내용을 더 추가하는 형태로 함수를 overriding 할 수 있고, 함수를 호출시에도 overriding으로 숨겨진 함수를 호출할 수 있다.

     

    C++ 11에서 override와 final 키워드

    다른 객체 지향언어에서 지원하는 override와 final 키워드는 C++에서는 11부터 지원한다.

    ovrride는 함수를 overriding 하는 경우 필수적이지 않다. 다만, 함수를 오버라이딩하는 경우 함수의 마지막에 override를 붙이기 되면, 컴파일러가 제대로 된 오버라이딩이 일어났는지 확인해줄 수 있다. 쉽게말해 함수의 오버라이딩에 관해 컴파일러에게 확인을 떠넘기는 것이다.

    final 키워드는 자바의 final과 같이 이 함수(또는 클래스)가 더 이상 상속, 오버라이딩 되지 않는다는 것을 의미한다. 따라서 final 키워드가 붙은 함수나 클래스를 오버라이딩/상속할 경우 컴파일러가 판단하여 컴파일오류를 띄운다.

    이 둘의 공통점은 개발자가 확인해야될 오류를(주로 런타임에 일어나는..) 컴파일러에게 떠넘긴다는 것이다.

     

     

    가상 소멸자

    ...추후 내용추가

     

    가상함수의 활용

    ,,,

    댓글