본문 바로가기
개발 언어/cpp

CPP 가상 함수(Virtual Function)

by eeeun:) 2022. 1. 18.
반응형

가상 함수(virtual function)란?

부모 클래스(기본 클래스)에서 선언되어 자식 클래스(파생 클래스)에 의해 재정의되는 멤버 함수

가상 함수는 기본 클래스 내에 virtual 키워드로 함수를 선언

 

가상 함수를 왜 사용할까?

가상 함수를 사용하지 않으면 오버라이딩 시에 문제가 있을 수 있음!

자식 클래스에서 포인터 변수로 객체에 접근할 때 의도치 않게 동작할 수 있다.

cpp에서는 컴파일러는 포인터 변수가 가리키고 있는 변수의 타입을 기준으로 함수를 호출하지 않는다.

포인터 타입을 기준으로 함수를 호출!

 

아래 코드를 보자.

class A {
public:
	void check() {
    	std::cout << "A : parent class" << std::endl;
    }
};

class B:public A {
public:
	void check() {
    	std::cout << "B : child class" << std::endl;
    }
};

int main() {
    A *p;
    A a;
    B b;
    
    p = &a;
    p->check(); // A : parent class
    p = &b;
    p->check(); // A : parent class
}

 

main문에서 처음에 p에 a객체의 주소를 줘 A class의 check()가 원하는 대로 잘 출력된다.

다음으로 p에 b객체의 주소를 줘 check()를 출력해보면, B class의 check()가 아닌 A class의 check()가 실행된다.

이와 같은 상황이 위에서 말한 cpp 컴파일러가 포인터의 타입을 기준으로 함수를 호출하는 예시이다!

 

이를 해결하기 위해 virtual를 쓴다.

virtual를 사용하게 되면 컴파일 시간에 함수가 결정되는 것이 아닌 런타임 시간에 함수가 결정되기 때문에 위와 같이 상황을 해결할 수 있다!

 

해결 후 코드이다.

class A {
public:
	virtual void check() {
    	std::cout << "A : parent class" << std::endl;
    }
};

class B:public A {
public:
	void check() {
    	std::cout << "B : child class" << std::endl;
    }
};

int main() {
    A *p;
    A a;
    B b;
    
    p = &a;
    p->check(); // A : parent class
    p = &b;
    p->check(); // B : child class
}

 

가상 함수 테이블 (Virtual Function Table = vtable)

cpp 컴파일러는 한 개 이상의 가상 함수를 포함하는 클래스에 대해 가상 함수 테이블(vtable) 만든다.

객체 안에 만들어지는 vtpr 포인터는 가상 함수 테이블의 시작 주소를 저장한다.

가상 함수 테이블에는 key와 value가 있다.

key = 호출하고자 하는 함수를 구분 지어주는 식별자

value = 구분자에 해당하는 함수의 주소

 

Key Value
void A::check() 0x2042

A 객체를 생성하면 객체에 A 클래스의 가상 함수 테이블의 주소 값(vtpr)이 저장된다.

A 객체의 check() 호출 시 다음과 같은 참조 과정을 가진다.

A 객체의 check() 호출 -> A 객체 vtpr의 가상 함수 테이블 시작 주소 참조 ->  가상 함수 테이블의 함수 주소 참조 -> 0x2042번지 실행

 

가상 함수 선언 규칙!

  • 클래스의 public에 선언
  • 가상 함수는 static 불가능
  • 다른 클래스의 friend 함수 불가능
  • 가상 함수는 실행시간 다형성을 얻기 위해 기본 클래스의 포인터 또는 참조를 통해 접근
  • 가상 함수의 프로토타입(반환형과 매개변수)은 기본 클래스와 파생 클래스에서 동일
  • 클래스는 가상 소멸자를 가질 수 있지만 가상 생성자를 가질 수 없음

 

728x90

댓글