Ch13 상속

7 분 소요

상속

대부분의 이야기는 다 아는 이야기일 것임.

헷갈리거나 다시 정리하기 좋은 부분만 정리함.

동적결합과 가상함수

함수를 호출하면 바로 다음줄의 코드가 아니라 멀리 떨어진 어떤 블럭의 코드를 실행 해야함.

이를 컴파일러 가 해주면 정적결합(Static Binding) 이라고 하고 런타임 에서 연결되면 동적결합(Dynamic Binding, Lately Binding) 이라고 함.

동적결합은 가상함수에서 쓰임(다른경우가 또 있는진 몰겠음)

당연히 동적결합이 더느림.

Virtual Function Table

c++ 에서 동적결합이 어떻게 동작하는가? 혹은 가상함수가 어떻게 동작하는가? 에 대한 답.

사실 컴파일러가 다르게 구현해도 결과만 같으면 상관이 없는데 대개 이렇게 구현한다함.

가상함수가 정의된 클래스는 virtual table 을 가지며 sizeof 로 직접 확인 가능함.

class AAA {
	int64_t a; 
};
class BBB {
	virtual ~BBB();
	int64_t a;
};

int main()
{
	cout << sizeof(AAA) << endl;
	cout << sizeof(BBB) << endl;
}
8
16

환경에 따라 다른데 Visual Studio 64bit 환경에서 돌리면 저렇게 나옴.

위 결과는 정확하게 말하면 Virtual Function Table 을 가리키는 VPointer 가 클래스의 멤버로 생기고 이게 위의 sizeof 에서 잡힌 것임.

VPointer 는 가상함수가 적용된 클래스마다 컴파일러가 생성한 VTable 의 주소를 가짐.

VTable 은 RTTI 정보를 가지며 Function Name 등을 인덱스로 해당 Function 의 시작 주소를 연결해줌. ( 뭐가 인덱스인지는 컴파일러 구현하기 나름임 )

flowchart LR A(객체) --> B(객체 멤버인 VPointer) --> C(정적으로 클래스마다 있는 VTable) --> D(Func Name 등을 Index 로 옳바른 함수 찾기) class A,B,C,D common;

가상함수 주의점

앞에서 Function Name 으로 함수의 주소를 찾아서 그런지는 모르겠지만 함수의 이름은 같은데 변수만 가지고 장난치는 오버로딩이 가상함수에선 민감함.

  1. 가상함수의 파라미터를 바꿀 수 없음. 만약 바꾸면 그건 override 가 아니라 새로운 함수를 만드는거임. 이때 예외가 있다면 리턴형에 한해서 기초클래스의 참조나 포인터면 파생클래스의 참조나 포인터가 가능함(covarriance).

  2. 가상함수가 오버로딩되어 있을 때, 하나만 override 할 수 없고 모든 버전을 다 해야함. 하나만 하면 나머지는 호출할 수 없게 됨.

추상 클래스

순수가상함수가 하나라도 있으면 추상클래스. 필수는 아니고 class AAA abstract 이렇게 표시가 권고됨.

추상클래스는 파생클래스가 순수가상함수가 전부 오버라이드 하지 않으면 객체가 될 수 없음.

인터페이스

  1. 순수가상함수로만 이루어지고

  2. 생성자 소멸자 연산자, static 없고

  3. 데이터 멤버 없음

다른 종류의 인터페이스 끼리는 서로 겹칠일이 없어서 다중상속이 용이해져서 쓴다고 함.

컴파일러가 자동으로 생성하는 멤버함수

디폴트 생성자, 디폴트 소멸자, 복사생성자, 복사대입연산자, 이동생성자, 이동대입연산자

reference 나 const 의 멤버가 있는 경우 복사대입연산자가 자동으로 안만들어짐.

이렇듯 모든 경우에서 다 만들어지는건 아님.

동적할당을 하는 클래스

Rule Of Five 라고 깊은 복사할 때 따르라는 규칙? 비슷한게 있음.

복사 생성, 이동 생성, 복사대입연산, 이동대입연산, 소멸자 중에 하나라도 명시적으로 선언하면 다 해라는 그런건데 표준은 아님. 깊은 복사 까먹지 말라는 지침 같은거.

댓글남기기