개발자 면접 공부/C-C++

C++ Virtual(가상) 함수

chogyujin 2022. 7. 12. 20:52
728x90

오늘은 Virtual 함수 가상 함수에 대해 공부하도록 하겠습니다.

가상 함수란?
- 기본 클래스 내에서 선언되는 파생 클래스에 의해 재정의되는 맴버함수
가상 함수를 호출하고 파생 클래스의 함수를 실행할 수 있습니다.
주로 실행시간에 함수의 다형성을 구현하는데 사용됩니다.
가상 함수는 기본 클래스내에 Virtual 키워드로 함수를 선언합니다.


가상 함수 선언에는 몇가지 규칙이 존재합니다.
1. 클래스의 공개(public) 섹션에 선언합니다.
2. 가상 함수는 정적(static)일 수 없으며 다른 클래스의 친구(friend) 함수가 될 수도 없습니다.
3. 가상 함수는 실행시간 다형성을 얻기위해 기본 클래스의 포인터 또는 참조를 통해 접근(access)해야 합니다.
4. 가상 함수의 프로토타입(반환형과 매개변수)은 기본 클래스와 파생 클래에서 동일합니다.
5.클래스는 가상 소멸자를 가질 수 있지만 가상 생성자를 가질 수 없습니다.


코드

#include<iostream>

using namespace std;

class Base
{
public : 
	void print()
	{
		cout << "base" << endl;
	}
};

class child : public Base
{
public:
	void print()
	{
		cout << "child" << endl;
	}
};

int main()
{
	Base* b;
	child c;
	b = &c;

	b->print();
}

이런 경우에는 Base에 실제 객체는 child로 가있습니다.
하지만 virtual이 선언이 안되있으면 Base 포인터는 이미 컴파일에 의해 자기 자신의 print를 호출하게됩니다.

하지만 이렇게 바꾼다면 어떤 현상이 벌어질가요?

#include<iostream>

using namespace std;

class Base
{
public : 
	virtual void print()
	{
		cout << "base" << endl;
	}
};

class child : public Base
{
public:
	void print()
	{
		cout << "child" << endl;
	}
};

int main()
{
	Base* b;
	child c;
	b = &c;

	b->print();
}

이러한 경우에는 virtual이 선언이 되있으므로 런타임에 의하 그 값이 결정이 납니다.

이러한 결과를 나타내는 이유는 가상 함수 테이블이라는 것인데요
virtual을 선언을 하게 되면 가상 함수테이블을 만들어 함수내에 가상 함수 포인터를 담게 됩니다.
그 가상 함수 포인터는 각각 런타임에 해당 클래스 함수 주소를 할당하여 관리하고있습니다.

처음에 new를 통해 힙 동적 할당을 할경우

처음에 new를 통해 힙 동적 할당을 할 경우 가상함수 태이블에 가상 함수 포인터 는 base에 print 를 가르키고있습니다. 이 또한 런타임에 동적으로 저장해줍니다.

b가 child c를 가르킬때

그 다음 b가 c를 가르킬때는 가상 함수 테이블에 가상 함수 포인터는 child에 print 를 가르키고 있습니다.

또한 저 코드는 매우 위험한 코드입니다. new를 하여 동적할당을 했고 바로 child인 c를 가르키면 동적할당된 메모리가 지워지지 않았기 때문에 메모리 누수가 생깁니다. Delete를 생활하 합시다.