본문 바로가기
C++ STL, 알고리즘

Virtual

by wanna_dev 2024. 10. 7.

베이스클래스에 virtual 키워드로 선언된 메서드만 파생 클래스에서 오버라이드 할 수 있다.

C++에서 클래스를 컴파일하면 그 클래스의 모든 메서드를 담은 바이너리 객체가 생성된다. 그런데 컴파일러는 virtual로 선언되지 않은 메서드를 호출하는 부분을 컴파일 시간에 결정된 타입의 코드로 교체한다.

이를 정적 바인딩이라고 한다.

메서드를 virtual 로 선언하면 vtable 이라 부르는 특수한 메모리 영역을 활용해서 가장 적합한 구현 코드를 호출한다. virtual 메서드가 하나 이상 정의 된 클래스마다 vtable이 하나씩 있는데, 이 클래스로 생성한 객체마다 이 vtable에 대한 포인터를 갖게된다. virtual 메서드의 구현 코드에 대한 포인터는 바로 vtable에 담겨있다. 그래서 객체에 대해 메서드를 호출하면 vtable을 보고 그 시점에 적합한 버전의 메서드를 실행한다. 이른 동적바인딩이라고 한다.

class Base{
	public: 
		virtual void func1(){}
		virtual void func2(){}
		void nonVirtualFunc(){}
};

class Derived : public Base{
public: 
	virtual void func2() overrid{}
	void nonVirtualFunc(){}
}

이 상태에서 인스턴스 두개를 생성한다

Base myBase;
Derived myDerived;

이렇게 생성한 두 인스턴가 vtable에 표현된 모습을 보여준다. myBase객체는 vtable에 대한 포인터를 갖고 있으며, 이 vtable에는 func1()과 func2()에 대한 항목이 있다. 각 항목은 Base::func1() 과 Base::func2()의 구현 코드를 가리킨다.

myDerived도 마찬가지로 vtable에 대한 포인터를 가지며, func1()과 func2()에 대한 항목으로 구성된다. 그런데 Derived가 func1()을 오버라이드하지 않기 때문에 func1()에 대한 항목은 Base::func1()을 가리킨다. 반면, func2()에 대한 항목은 Derived::func2()를 가리킨다.

여기서 주목할 점은 두 vtable모두 nonVirtualFunc()메서드에 대한 항목을 가지지 않는다는 점이다.

이 메서드는 virtual 로 선언되지 않았기 때문이다.

virtual 소멸자의 필요성

메서드를 모두 virtual로 선언하는 방식에 반대하는 프로그래머도 소멸자 만큼은 virtual로 선언해야한다고 생각한다.

소멸자를 virtual로 선언하지 않으면 객체가 소멸할 때 메모리가 해제되지 않을 수 있기 때문이다.

클래스를 final로 선언할 때를 제외한 나머지 경우는 항상 소멸자를 virtual로 선언하는 것이 좋다.

예를들어 파생 클래스의 생성자에 동적으로 할당된 메모리를 사용하다가 소멸자에게 삭제하도록 작성했을때 소멸자가 호출되지 않으면 메모리가 해제되지 않는다. 마찬가지로 std::unique_ptr처럼 파생 클래스에 자동으로 삭제되는 멤버가 있을때 그 클래스의 인스턴스가 삭제될 때 소멸자가 호출되지 않으면 이런 멤버가 삭제되지 않고 남게 된다.

//virtual로 선언되지 않은 소멸자가 호출되지 않는 경우
class Base{
public: 
	Base(){}
	~Base(){}
}
class Derived:public Base{
pulbic:
	Derived(){
		mString = new char[30];
		cout<<"mString allocated"<<endl;
	}
	~Derived(){
		delete[] mString;
		cout<<"mString deallocated"<<endl;
	}
private:
	char* mString
}

int main(){
	Base* ptr = new Derived();
	delete ptr; // ~Base만 호출
	return 0;
}


참조 : 전문가를 위한 c++

'C++ STL, 알고리즘' 카테고리의 다른 글

가상 베이스 클래스 (다이아몬드 상속 해결)  (0) 2024.10.07
파생 클래스의 복제 생성자와 대입 연산자  (0) 2024.10.07
스마트 포인터 c++  (0) 2024.10.07
memory leak detection  (0) 2024.10.02
C++ 상속  (3) 2024.10.02