Back-end/C++

22. 03. 25 - 상속( 접근 제어자, 업 캐스팅, 다운 캐스팅 )

giggs 2022. 4. 3. 17:54

 

상속관계에서 접근제어자의 의미

 

 

 

상속받을 때 부모의 속성을 어떤 형식으로 물려받을 것이냐

 

 

 

A 클래스를 public 으로 상속받은 B 클래스

 

 

 

 

class B : public A { }

 

  • public 속성은 – 부모의 모든 것 물려받겠다. - 부모의 public 영역 다 사용하겠다.
  • protected 해주면 – 부모의 public 영역을 protected로 상속받겠다. - 부모 영역 접근 불가
  • private 해주면 – 부모의 public, protected영역을 private로 상속받겠다. - 부모 영역 접근 불가

 

 

 

다른 언어에서는 접근제어자 사용하는 경우 본 적 없다.

 

 

 


 

 

 

상속관계에서의 업 캐스팅과 다운 캐스팅

 

 

 

업 캐스팅

  • 상속을 하게 되면 어떤 특성이 생기냐?
  • 자식의 객체를 부모의 타입으로 받을 수 있다 = 업 캐스팅 

 

 

 

A, B타입 객체 생성 후 업캐스팅, 다운 캐스팅

 

 

 

 

일반적인 관계에서

  • A타입 a객체 만들고 /// B타입 b객체 만들고 
  • a = b; 불가능 - ( b를 a에 대입 )
  • b = a; 불가능 - ( a를 b에 대입 )
  • 객체 타입이 다른 A와 B는 서로 대입 불가능

 

 

 

 


 

 

but !!

 

상속관계에서는

  • a = b; 가능 - 업 캐스팅 
  • b = a; 일반적인 경우 불가능 - 다운 캐스팅 

 

 

 

why ??

 

 

메모리 공간으로 확인해보자.

 

 

현재 메모리 상태 

  • A타입 a객체에 _value = 10 저장
  • B타입 b객체에 _value = 20 , _value1 =30 저장되어있는 상태

 

 

 

자식 클래스는 부모의 것 + 자신의 것으로 이루어져있다. 따라서 부모의 내용 채워줄 수 있다.

 

 

 

 

a=b; 업 캐스팅 가능

  • a = b; 하면 -> 부모 A타입 a객체의 _value 값이 -> 자식 B타입 b객체가 가지고 있던 value = 20으로 변경되면서 가능!
  • 자식은 부모 것도 가지고 있고, 자신 것도 가지고 있으니 부모에게 대입해줄 수 있다.

 

 

 

b = a; 다운 캐스팅은 안된다.

  • 부모인 A타입 a객체는 _value의 값만을 가지고 있는데 
  • 자식인 B타입 b객체는 _value와 _value1을 가지고 있다. 
  • 부모가 가지고 있는 값으로 자식의 모든 공간을 채워줄 수 없다.
  • 일반적인 경우 부모의 클래스보다 자식 클래스가 더 많이 가지고 있으므로,
  • 특별한 경우를 제외하고는 다운 캐스팅 불가능

 

 

 

 

정리

  • 자식은 부모 것도 가지고 있고 자신 것도 가지고 있으니 부모에게 대입해줄 수 있지만
  • 부모는 자식이 가진 것을 채워줄 자료가 없다. - 다운 캐스팅 불가능

 

 

 


 

 

 

 

다운 캐스팅이 가능하게 해 주려면?

 

객체를 받을 때 참조형이나 포인터형으로 받아야 한다.

 

 

 

업 캐스팅 시 ( a = b; ) 하면 대입은 되지만, 

b객체의 _value1 부분이 사라진다. - 값의 유실이 생긴다 - warring 상황

 

 

그러므로!

b객체를 A타입으로 받을 경우 참조형이나 포인터형으로 받아야 한다!!

 

 

 

A타입으로 객체를 받는데 참조 값으로 받겠다 / 포인터 형으로 받겠다.

 

 

 

 

참조형이 아닌 대입( A a = b; )으로 그냥 해버리면 b객체의 유실된 값 찾을 수 없다.

 

 

 


 

 

 

참조형 / 포인터형으로 받으면 어떻게 되나 확인해보자

 

 

참조형 - 업 캐스팅 상황

 

 

 

객체 b를 받을 때 - b의 값이 아닌 - b의 주소 값으로 받는다! - b의 주소 값은 b 객체 전체를 가리킨다.

 

 

 

30번 Line - (  a=b  )가 아닌 ( &refb = b )

30번 Line - B타입 객체 b를 - A타입으로 업 캐스팅해주는데 값이 아닌 - 주소 값을 참조하는 방법으로!

30Line b를 &refb로 받았으니까 &refbb객체 전체를 가리킨다

30Line 그런데 refb를 - A타입으로 만들었으니까 b객체의 _value 부분에만 접근 가능하다.

30Line b객체 전체를 가리키지만 데이터 타입 때문에 A영역인 _value에만 접근 가능

30Line A타입으로 만들었다고 값이 없어진 것은 아님!

 

 

 


 

 

참조형 - 다운 캐스팅 상황

 

 

A 타입으로 만들었던 refb 객체를 - 다시 B 타입으로 다운 캐스팅

 

 

 

33번 Line - ( b=a )가 아닌 ( &refbb = (B&) refb )

33Line A 타입인 &refbB 타입으로 형 변환 후 대입 -  

33Line - &refb는 b객체 전체이다. B타입으로 다운 캐스팅해줘도 채워줄 수 있다. - 다운 캐스팅 가능

 

 

 


 

 

 

포인터형도 마찬가지 - 업 캐스팅 상황

 

 

 

 

 

 

 

38번 Line - A* => A형 주소 값을 저장하는 공간 - pb라는 변수

38번 Line - 여기에 B형 주소 값 &b 이 들어온다. - 채워 줄 수 있으므로 가능 - 업 캐스팅 가능

38번 Line - 자식 타입의 주소 값 ((( B* 의 주소 값을 저장하는 공간 )))도 pb에 들어올 수 있다.

39번 Line - pb는 b객체 전체를 가리키지만 타입이 A타입이기 때문에 - b객체의 _value 부분만 접근 가능

 

 주소 값으로 접근 시 ( -> ) 연산자 사용

 

 

 

 


 

 

 

다운 캐스팅 상황

 

 

 

 

 

 

 

41번 Line - B* = B형 주소 값을 저장하는 공간 - pbb라는 변수에

41번 Line - 여기에 A형 주소 값 pb가 들어온다 -

41번 Line - pbA타입인데 (B*) B타입으로 다운 캐스팅 가능

 

 

why?

  • pb가 온전한 b객체를 가리키고 있으므로 - ( 데이터 유실 X ) - 접근 가능한 부분만 달라졌던 것
  • 원래 B타입이었던 애 A타입으로 갔다가 다시 돌아온 것

 

 

 


 

 

다시 설명

 

 

 

A클래스를 통해 a객체를 만들고 _value = 10;

A클래스 상속받은 B클래스를 통해 b객체를 만들고 _value = 20; , _value = 30;

 

객체 ab의 데이터 타입은 AB로 다르지만!

 

상속관계에서는 자식 타입으로 부모 타입에 적용 가능 업 캐스팅 가능 언제나 허용

자식은 부모 거를 가지고 있으므로 값을 채워 줄 수 있다.

다만, 자식이 추가로 가지고 있던 부분은 값이 사라진다. - 값이 유실된다.

 

상속관계에서 부모 타입으로 자식 타입에 적용 불가능 다운 캐스팅 불가능

부모는 자식 거를 가지고 있지 않으므로 값을 채워 줄 수 없다.

 

 

 

대입은 Right Value 를 Left Value 에 대입해주는 것.

 

 

a=b; 

  • B타입 b객체를 A타입 a에 대입하는 경우 - 업 캐스팅 가능 -채워줄 수 있으므로 가능
  • a객체의 _value 값이 20으로 바뀌고 b객체가 가지고 있던 _value1의 값은 없어짐

 

b=a;

  • A타입 a를 B타입 b에 대입하는 경우 - 다운 캐스팅 불가능 - 채워줄 수 없으므로 불가능
  • b객체의 _value값은 채워줄 수 있지만 _value1의 값은 채워줄 수 없다.

 

 

이처럼 원래 B타입이었던 b객체를 값을 대입하는 형식으로 A타입으로 변경하면 데이터의 유실이 생기고

다시 B타입으로 변경하려고 하면 없어져 버린 부분 때문에 채워 줄 수 없어서 형 변환이 안된다.   

 

 

이러한 상황을 만들지 않기 위해 업 캐스팅/다운 캐스팅 시에는/ 데이터의 유실을 방지하지 위해 

참조형이나 포인터형으로 하는 것이 좋다.

 

 

 

 


 

 

참조형

 

객체를 참조형으로 받는 경우

 

 

 

 

refb b 가 가리키는 것은 동일하다. - b객체 전체를 가리킨다.

 

But!

refbA타입으로 만들어진 것이다. - 그러므로 b객체의 A타입 부분만 접근 가능

 

 

 

 


 

 

 

 

포인터형

 

 

 

객체를 포인터형으로 받는 경우

 

 

 

41Line - B타입의 객체 b의 주소 값 &b-> A타입 객체 주소 값 저장하는 공간 pb에 전달

41Line - pbA타입의 주소 값을 저장하는 공간이다. A*이지만 &b 가능! - 업 캐스팅 가능

 

41 Line - B타입이었던 &b를 업 캐스팅- A타입 pb로 만들어줌

42Line - A타입인 pb는 - b객체의 A타입 부분만 접근 가능( _value1 에는 접근 불가능 )

44Line - 원래 B타입이었다가 A타입이 된 pb를 - 다시 B타입으로 다운 캐스팅 가능!

44Line – 가능한 이유 – 업 캐스팅될 시 데이터 유실 X - 온전한 B 객체를 가리키고 있던 것이다.

접근 부분만 제어되었던 것. 데이터 온전히 있으니 다시 다운 캐스팅 가능

 

 

 

 


 

 

 

Question.   

 

 

 

 

 

 

Answer

 

 

원래는 B타입 주소 값 B* = &b

이것을 A타입 주소 갑 A* A*으로 바꾼 것이다. - 주소 값이 변한 게 아니라 타입만

이것을 다시 타입을 B타입 B*으로 바꾼 것

 

 

 

 


 

 

 

 


review



상속관계에서의 접근제어자
부모의 속성을 어떤 형식으로 물려받을 것인가를
정할 수 있는 C++의 특징! 

업 캐스팅 다운 캐스팅 부분에서는
java의 묵시적 형 변환과 명시적 형 변환이 생각났다.
업캐스팅 시에는 자동으로, 다운 캐스팅시에는 명시적으로!

다른 점은 역시 C++의 참조형과 포인터형!
java에서는 다운 캐스팅 시에 논리적 오류를 잡기 위해 instance of를 사용했었는데
C++에서는 참조형과 포인터형으로 객체를 받아버리니까
체크할 필요가 없었다.

이번 강의에서 특히 좋았던 점은
"상속관계에서 업 캐스팅은 되고 다운 캐스팅은 참조형/포인터형을 사용해야 됩니다."
로 끝난 것이 아닌 객체를 값으로 받으면 다운 캐스팅이 왜 안되는지,
왜 참조형과 포인터형으로 객체를 받아야 하는 지를
내부적으로, 메모리 상황으로 알 수 있어서 좋았다.



## Check!

참조형
업 캐스팅 : A& refb = b;
다운 캐스팅 : B& refbb = (B&) refb;

포인터형
업 캐스팅 : A* pb = &b;
다운 캐스팅 : B* pbb = (B*) pb;