[C++] virtual 내부 호출

연두비두밥·2024년 2월 7일
post-thumbnail

코드 분석을 하다가 헷갈리는 부분이 있어서 기록

[ KeyPoint ] virtual 된 함수를 부모의 그냥 함수에서 호출하면 어떻게 될까?

우리는 이미 오버라이딩에 대해 알고 있다.
간단하게 오버라이딩과 virtual의 차이에 대해 알아보면서 시작해보자.


오버라이딩

class A
{
public:
	void OverTest()
    { cout<<"Hi My name is A\n"; }
}

class B : public A
{
public:
	void OverTest()
    { cout<<"Hi My name is B\n"; }
    
void main()
{
A* a = new A();
B* b = new B();
a->OverTest();
b->OverTest();

를 동작하게 된다면 어떻게 될까?

오버라이딩 되어서 원하는 결과를 얻었다.

하지만 여기서 아래와 같이 부모포인터에 자식 포인터를 할당해서 OverTest에 접근을 한다면?

A* a1 = new B();
a1->OverTest();

virtual

부모 포인터가 불리게 된다. 이런 경우 원하는 B를 찾기 위해 즉, 다형적으로 동작하기 위해 virtual 키워드를 붙인다.

class A
{
public:
	virtual void OverTest()
    { cout<<"Hi My name is A\n"; }
}

class B : public A
{
public:
	virtual void OverTest() override
    { cout<<"Hi My name is B\n"; } 

override 키워드는 붙여도 되고 안붙여도 되는 걸로 알고 있다. 하지만 코드를 읽는 사람이 직관적으로 알 수 있게 명시적으로 붙여주는게 좋다.
위와 같이 작성된 코드를 동일하게 돌려준다면

A* a1 = new B();
a1->OverTest();

원하는 결과를 얻을 수 있다. 이는 virtual의 키워드가 붙는 순간 클래스 앞에 virtual function table이 생성되고 그 포인터가 놓이게 된다. 이를 바탕으로 함수가 호출될때, 즉, 컴파일 타임에 어떤 함수를 호출할지 찾아서 호출한다.


위의 경우는 이미 알고 있었던 내용이지만 한번 더 정리했다. 여기서 가장 헷갈리고 궁금했던 점은 이렇게 virtual된 함수를 부모 객체에서 virtual이 아닌 함수에서 호출하면 어떤 함수가 불릴지였다.

이를 위해 테스트를 해보았다.

class A
{
public:
	void totalTest()
    { test(); }

	virtual void test()
    { cout<<"This is A\n"; }
};

class B : public A
{
public:
	virtual void test() override
    { cout<<"This is B\n"; }
};

class C : public B
{
public:
	virtual void test() override
    { cout<<"this is C\n"; }
};

로 작성하였고, 테스트는

	A* a = new C();
	a->totalTest();
	a->test();

	A* a1 = new B();
	a1->totalTest();
	a1->test();

	A* a2 = new A();
	a2->totalTest();
	a2->test();

	B* b = new C();
	b->totalTest();
	b->test();

	B* b1 = new B();
	b1->totalTest();
	b1->test();

	//B* b2 = new A(); 
    //당연하게 문제가 발생 자식 포인터에 부모 포인터를
    //할당하려고 했으니깐

	C* c = new C();
	c->totalTest();
	c->test();

아래와 같은 결과를 얻을 수 있었다.
즉, 부모 포인터에 넣는 자식의 함수가 호출되는 것을 확인 할 수 있었다. 나는 그 전까지는 외부에서 부르는 것만 해보았기 때문에 내부에서 부르면(아무리 자식포인터를 할당했다고 해도) 부모의 함수가 불리지 않을까 생각했는데, 생각해보면 컴파일 타임에 가상 함수 테이블이 불리면서 할당된 포인터에 접근하여 함수를 호출하는것 같다.

profile
꾸준하고 싶은 사람

0개의 댓글