본문 바로가기

Language/C++

Derived class Point to Base class Point variable


먼가 제목이 포스가 있다.ㅋㅋㅋㅋ

음...별거는 아니고, 있는 그대로 해석을 해보면 Derived class 의 포인터를 Base class 의 포인터 객체에 넣는다! 라는 이야기다.

이거 좀 중요한데...제목을 PPT 제목과 그대로 가는 바람에....않보면 자기손해지....그렇지!

일반적으로 C++에서는 어떤 자료형의 주소를 다른 자료형 의 포인터에 대입을 하도록 허용하지 않는다.

먼말이냐고?

double a;

int *b = &a;

이런걸 허용하지 않는다는 것이다.

하지만...예외처리는 항상 존재하는 법이다(-by 기재표)

예외의 경우로 상속관계의 class 는 대입이 가능하다.

상속이라는 것은 어떠한 예외도 비켜가는 것이다. 솔직하게 책에도 기제가 되어있지만, 이딴 기능을 굳이 만들어 공부를 할때 머리를 아프게 하는지 이해가 않되는 사람들이 꽤 있다.

이러한 기능은 나중에 조금더 언급이(아주 조금..) 되겠지만 Visual C++ 에서 굉장히 강력하게 사용이 되기때문이다.

책에서는 비유를 가족에 비유를 했는데 자료형이 달라도 받아 드리는거 처럼...아무리 말도 않되는 거도...가족은 이해해주는 것이다..

자 본론으로

한술 더 떠서...Base class에서 Derived class로 형 변환(cast)도 가능하며 이를 형 격하 라고 한다.

Base x(10,30);
Add *pAdd;
pAdd = &x;
pAdd = (Add *)&x;

Derived class to Base class ===> 3 번째 라인이다. 이런경우는 묵시적인 형변환이 일어 나기 때문에 그냥 밀어넣어도 무관하다.

Base class to Derived class ===> 4 번째 라인이다. 이런경우는 명시적인 형변환이 필요하기 때문에 앞에 반드시 cast 연산자를 사용하여 명시를 해줘야 한다. (일어나기 때문에 와 필요하기 때문에를 구분 잘하여야 한다.)

조금 더 설명을 하자면, 저장되는 객체가 무엇인가 에 따라서 호출되는 영역이 틀리다. 상속관계의 상위로 갈수록 호출 할 수 있는

멤버 함수가 작아지며, 마찬가지로 하위로 갈수록 호출할 수 있는 멤버함수도 많아진다. 또한, 포인터 객체가 최 상위 클래스 라고 해도

저장된 객체가 어떤가에 따라서 또 수가 틀리다. 만일 최 상위 클래스의 포인터 객체에 최 하위 클레스의 객체의 주소를 담으면?

오만함수 다 호출 가능하다.

이러한 인터페이스를 지원하는 이유는 VC++에서 그 이유가 분명하다.

CWnd 클래스의 포인터 객체..*pWnd 에 의해 생성된 pWnd라는 포인터 객체를 보면, 이 객체는 대화상자(CDialog class)는 물론 버튼(CButton class) 의 주소도 대입하여 인터페이스를 할 수 있다.

이게 왜....머가 중요한데...라고 할 수 도있는데...이걸 다 설명 하려면 VC++인 MFC의 구조와 스타일을 다 설명 해야한다.

그래서 앞에서 언급 했듯이 아주 조금만 언급한것... 저런식으로 상속된 관계 끼리 포인터 대입을 허용하면 굉장히 유연한 프로그래밍을 할 수 있다는 것이다. (잘모르겠어? MFC를 해봐...그럼 알아.ㅡㅡ)

자...그럼 위의 소스에서 pAdd로 prn()(이 함수는 많이 썼으니까 대충 멀하는 건지 알꺼라고 생각한다.)을 호출한다고 하면, 어떻게 될까?

각자 가지고 있는 상속class를 공부했던 소스를 가지고 Test를 해보면 알겠지만(꼭 좀 해보길 바란다. 눈으로만 보지말고....코딩의 실력은 누가 키보드를 많이 두드려 보느냐이다...(-_ㅡ 문디 지도 않하는기...ㅋㅋㅂㅅㅋㅋ)..ㅡㅡ 어째든.ㅋㅋ)

pAdd로 호출을 하게 되면, Derived class의 prn()이 호출이 되게 된다. 허허~~우리가 원하는건 이게 아니지요...

왜 이런 일이 생길지는 아는 사람은 알고 갸우뚱 하는 사람은 좀 혼란 스러울 꺼라고 생각한다. 자..자...이렇게 우리는 Base class의 함수를 호출하려고 하였지만 컴파일러라는 망할 기계때문에 원하는 값이 나오지 않는다

왜그런가 알아보자.

C++의 컴파일러는 컴파일러가 중간언어를 만들기 위하여 (앜ㅋㅋ 컴파일러 공부를 쪼금 했죵?ㅋㅋ) 토큰을 파서가 구문분석을 하면서 에러를 검출 할때는 포인터 객체의 자료형 중심으로 생각을 하게 된다. (아...컴파일 이론을 접목하니까 심하게 말이 어렵네...)

간단히 얘기하면...(간단하지도 않지만 그래도 잘 이해하길..) 실제 객체가 어떤가는 컴파일러는 모르고, 포인터 객체의 Type에 맞추어서 호출을 한다는 것이다.!

자 여기서 포인터 객체는 객체를 선언할때 바로 위 소스의 2번 라인에서 pAdd의 자료형은 Add 이며 이때 객체는 실제 객체가 된다.

그리고 실제 객체는 바로 첫 번째 라인이 x의 주소값이 즉, 실제로 가리키고 있는 값이 되는 것이다.~

사실 실제 객체가 어떤가가 중요한 것이며, 포인터 객체는 단지 가리키고 Direct가 아닌 Reference(참조)를 해주는 역활뿐 포인터 객체가 주가 되어 버리면 않된다.

바로 이게 모순이며, 이치에 맞지 않는 거다. 

좀 이상하다.

다시 정리를 해보면 컴파일러는 포인터 변수만 보고 사용가능한 멤버 함수를 결정하며, 때문에 포인터 Derived class 객체라면, 저장된

주소가 Base class의 객체의 주소라고 하여도, Derived class의 멤버 함수가 호출이 되어 버린다는 것이다.

눈치가 빠르고, 좀 많이 배우고 열심히 공부를 한 사람들은 눈치 깠네....피씩 웃기는.ㅋㅋㅋㅋ아래 글에서 언급조금 해주겠다.

잘 모르겠다 는 사람이 있을꺼라고 생각하고 (이 글 누가 보기는 하나???(음...나중에 내가 볼꺼야....ㅡㅡ;;))

포인터 객체(int *a)의 자료형에 대한 결정은 Compile Time(Static Binding or eraly Binding) 에 결정이 되며,

실 객체(a = &b 에서 &b에 대한 자료형)의 자료형에 대한 결정은 Ren Time(Dynaminc Bindin or late Binding) 에 결정이 나기 때문이다.

 

사실 이번 오버라이딩 부터 동적 바인딩 그리고 위에서 계속 나올 가상 함수등은 원래 한 파트로 연결이 되어있다. 하지만

너무 글이 길면 지루하기 때문에 소 파트로 나누어서 하는데 연결이 좀 심한듯?...

이 파트에서 가장 중요한것은 class가 상속으로 묶여있다면, 서로 포인터를 주고 받을수 있다는 것을 명심하고 이를 이용할수 있는 길이 있으면 주저 말고 이용하여 유연한 프로그래밍을 하라~ 는 것이다.^^

그리고 또한 이 글을 읽었으면 반드시 다음에 나오는 동적 바인딩과 가상 함수 부분도 공부를 하고 넘어가줬으면 한다.

소스는 중요한 부분이 아니면 올리지 않겠다. 번거롭기도 하겠지만 우선 책에 기본적으로 나와있는 걸 가지고 공부를 하면 되니까ㅋ

 -C++언어 30일 완성 참조, A-Shell Study team 유창PPT 참조-