C++ 객체지향프로그래밍 수업
[C++/polymophism] 2. overriding된 함수를 사용하려면 virtual로 선언해 주어야 한다.
인생 걸고 삽질
2022. 11. 9. 01:04
7주차 금요일 수업
오늘은 virtual function(가상함수)에 대해 배운다.
function overriding
함수의 overriding이란 자식 class에서 부모 class의 함수를 재정의하는 것이다.
- 부모 class에 있는 함수가 자식 class에서 사용할 때는 다른 행동을 하도록 만들 수 있다. 이렇게 부모 class에 있는 함수를 자식 class에서 재정의하는 것을 ovrride한다고 한다.
- 함수의 overloading이 어떤 함수의 signature를 다르게 해서 이름은 같은데 기능은 다른 함수를 한 개 더 만들어 주는 거라면, overriding은 부모 객체에서 선언한 함수를 자식 객체에서 아예 다시 정의하는 거다.
- 그래서 overloading과는 다르게 함수를 overriding할 때는 prototype(return type과 signature)을 완전히 같게 해줘야 한다.
필수는 아니지만 뒤에 override 키워드를 붙여주면 좋다.
- 장점 1: 코드를 보고 명시적으로 이 함수가 overriding 되었음을 알 수 있다.
- 장점 2: 만약 signature를 잘못 입력해 줬더라도, 제대로 overriding되지 않았다면 오류를 뿜어 준다.
- 함수를 외부에서 정의할 때는 ovrride 키워드를 빼야 한다. static처럼...
함수 뒤에 final 키워드를 써주면 이게 마지막 overriding이라는 뜻이다.
- 그 뒤에서 그 함수를 한 번 더 ovrriding하려고 하면 오류를 뿜어 준다.
virtual function
부모 class의 함수 대신 overriding된 함수를 사용하려면 처음부터 virtual로 선언해 준다.
- Base class의 pointer를 이용해 derived class의 function을 사용하고 싶다면, 다음과 같은 코드는 근본없다.
#include<iostream>
using namespace std;
class Base {
public:
void print() { cout << "BASE"; }
};
class Derived :public Base {
public:
void print() { cout << "DERIVED"; }
};
int main() {
Base* basePtr = new Derived;
basePtr->print(); //출력: BASE
}
- 왜냐하면 위의 경우 basePtr의 Data type은 Base이기 때문에, print()를 쳤을 때 Base class의 print() 함수가 실행된다.
- 이런 상황에서 virtual function의 개념이 나온다. virtual keyword를 붙여 주면, 부모의 함수는 그냥 통과하고 자식의 ovrriding된 함수를 실행한다.
- 위의 코드를 근본있게 수정하면 다음과 같다.
#include<iostream>
using namespace std;
class Base {
public:
virtual void print() { cout << "BASE"; }
};
class Derived :public Base {
public:
void print() override { cout << "DERIVED"; }
};
int main() {
Base* basePtr = new Derived;
basePtr->print(); //출력: DERIVED
}
virtual 속성은 상속된다.
- 그래서 부모의 function에만 virtual keyword를 붙여 줘도 되지만, 가독성과 안정성을 위해 그냥 자식의 function에도 무지성으로 virtual을 명시해 주는 습관을 들이자.
virtual function으로 만들어 놓고 ovirriding을 안 해주면 그냥 본인이 실행된다.
polymophism을 사용했다면 destructor도 virtual로 만들어 줘야 한다.
- 원래는 자식 node를 만들 때 부모의 constructor는 자동으로 실행되고, 자식 node를 다 썼을 때는 부모의 destructor도 자동으로 실행된다.
- 하지만 polymophism을 사용했다면 결국 base의 pointer가 가리키는 소멸자가 실행되므로 부모의 destructor는 실행되지만 자식의 destructor는 실행되지 않는다.
- 이러한 상황에서 메모리 누수가 발생하게 된다.
- 이 때 Base class의 Destructor를 명시적으로 virtual로 만들어 줘야만 자연스레 Derived class의 Destructor가 호출된다. Derived class의 Destructor는 원래 부모의 소멸자를 호출하니까...
Dynamic Binding과 Static Binding
Dynamic Binding은 overriding된 함수가 부모 함수인지 자식 함수인지를 실행 시에 결정한다.
- 포인터를 사용해서 ->로 함수를 실행한다.
- 교수님이 그냥... 가볍게만 생각하고 넘어가라고 하셨다.
Static Binding은 overriding된 함수가 부모 함수인지 자식 함수인지를 컴파일할 때 결정한다.
- 객체 자체를 사용해서 .으로 함수를 실행한다. polymophism까지 갈 것도 없이 당연히...
- 컴파일할 때 다 결정하고 가기 때문에 Dynamic Binding에 비해 효율이 좀 더 좋다고 한다.