2 minute read

1. 멤버함수와 가상함수 동작원리

✏️그것이 알고싶다: 멤버함수는 과연 객체 안에 존재하나?

  • 객체가 생성되면 멤버변수는 객체 내에 존재하지만, 멤버함수는 메모리의 한 공간에 별도로 위치하고선, 이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태를 취한다

  • 함수는 그럼 기본적으로 static이 선언된 것과 다름 없다는 건가?

✏️가상함수의 동작원리와 가상함수 테이블

  • 가상함수 : 포인터 변수가 실제로 가리키는 객체의 함수를 호출하게 함

  • 한 개 이상의 가상함수가 포함된 클래스의 객체가 생성될 때, 컴파일러는 가상함수 테이블을 만든다

      +----------+----------+
      |   key    |  value   |
      +----------+----------+
      |   Func   |  address |
      +----------+----------+
      |   Func   |  address |
      +----------+----------+
    
    • V-Table이라고도 한다. 실제 호출되어야하는 함수 위치정보를 담고있다.

    • 이때, 오버라이딩 된 기초클래스의 가상함수에 대한 정보는 유도클래스의 테이블에 포함되지 않는다

    • 객체 생성과 상관없이 메모리 공간에 할당된다

✏️가상함수 테이블이 참조되는 방식

failtoBring

출처: 네이버 블로그/치킨런: https://blog.naver.com/kbm0996/221114385074

  • 위와 같은 과정이 있기 때문에 실행속도가 감소하기 마련이다. 그러나 속도가 느려지는 미미한 단점보다 장점이 더 유용하여 사용하게 된다.

2. 다중상속에 대한 이해

✏️다중상속

  • 득/실에 대한 견해가 갈림

  • 모호성 : 기초클래스의 멤버 이름이 겹칠 경우 호출할 때 문제 발생

#include <iostream>
using namespace std;

class BaseOne
{
public:
	void SimpleFunc() { cout<<"BaseOne"<<endl; }
};

class BaseTwo
{
public:
	void SimpleFunc() { cout<<"BaseTwo"<<endl; }
};

class MultiDrived : public BaseOne, protected BaseTwo
{
public:
	void ComplexFunc()
	{
		BaseOne::SimpleFunc();      //출처를 밝히면 된다
		BaseTwo::SimpleFunc();
	}
};

✏️가상상속

#include <iostream>
using namespace std;

class Base
{
public:
	Base() { cout<<"Base Constructor"<<endl; }
	void SimpleFunc() { cout<<"BaseOne"<<endl; }
};

class MiddleDrivedOne : virtual public Base
{
public:
	MiddleDrivedOne() : Base()
	{
		cout<<"MiddleDrivedOne Constructor"<<endl; 
	} 
	void MiddleFuncOne() 
	{ 
		SimpleFunc();
		cout<<"MiddleDrivedOne"<<endl; 
	}
};

class MiddleDrivedTwo : virtual public Base
{
public:
	MiddleDrivedTwo() : Base()
	{
		cout<<"MiddleDrivedTwo Constructor"<<endl; 
	} 
	void MiddleFuncTwo() 
	{ 
		SimpleFunc();
		cout<<"MiddleDrivedTwo"<<endl; 
	}
};

class LastDerived : public MiddleDrivedOne, public MiddleDrivedTwo
{
public:
	LastDerived() : MiddleDrivedOne(), MiddleDrivedTwo()
	{
		cout<<"LastDerived Constructor"<<endl; 
	}
	void ComplexFunc()
	{
		MiddleFuncOne();
		MiddleFuncTwo();
		SimpleFunc();
	}
};

int main(void)
{
	cout<<"객체생성 전 ..... "<<endl;
	LastDerived ldr;
	cout<<"객체생성 후 ..... "<<endl;
	ldr.ComplexFunc();
	return 0;
}
  • 위 코드에서 LastDerived의 객체는 Base를 두개 갖고있는 셈이 된다.
    +------------------+   +-------------------+
    |   +-----------+  |   |  +-----------+    |
    |   |   Base    |  |   |  |   Base    |    |
    |   +-----------+  |   |  +-----------+    |
    |   +-----------+  |   |  +-----------+    |
    |   | MiddleOne |  |   |  | MiddleTwo |    |
    |   +-----------+  |   |  +-----------+    |
    +------------------+   +-------------------+
                \               /
                 \             /
                   LastDerived 
  • 이런 상황에서 Base클래스 멤버가 하나씩만 존재하는게 타당하기 때문에 가상상속을 통해 해결한다.

  • class MiddleDrivedOne : virtual public Base, class MiddleDrivedTwo : virtual public Base 이렇게 가상상속을 하는 클래스를 다중상속 받으면 멤버가 하나만 생기게 된다

    +--------------+        +--------------+
    |           +---------------+          |
    |           |     Base      |          |
    |           +---------------+          |
    |   MiddleOne  |        |   MiddleTwo  |
    +--------------+        +--------------+
               \                /
                \              /
                   LastDerived 

Tags: ,

Categories:

Updated: