6.10 클래스 타입 변환

phoenixKim·2021년 8월 20일
0

fundamental c++

목록 보기
1/14
post-thumbnail
  • fundamental c++을 공부하고 정리한 내용입니다.

c++의 4가지 형변환과 레퍼런스타입 형변환

  • 레퍼런스 타입 형변환은 메모리의 크기를 변경하는 것이다.
    만약에 부모에서 자식으로의 형변환이 이루어진다면, 자식의 멤버의 값들은
    어떻게 될지 미정이다.

static_cast

  • 안전하지 않다., 하향형변환이 이루어졌을때, 그에 따른 책임은 유저가
  • 컴파일 시간에 형변환이 이루어진다.
  • 하향형변환에서 value 타입으로 형변환이 이루어질때 컴파일러가 거부한다.
    왜냐하면 메모리 상으로 생각해보면, 부모는 작고, 자식 클래스는 부모를 상속했으니, 메모리가 더 클수밖에 없다. 자식의 멤버들의 값을 알수 없기 때문이다.
  • 하향형변환에서 레퍼런스 타입으로 형변환을 한다면, 가능하다.
    왜냐하면 레퍼런스 타입의 형변환은 메모리의 크기를 변경하는 것이기 때문이다.
    하지만, 그에 따른 미정값으로 되어 있는 멤버들의 책임은 유저가 해야한다.

reinterpret_cast

  • 강제성을 띄는 형변환이다.
  • 상속이든, 관계없는 클래스도 가능!
  • 하지만, 참조 및 포인터에서의 형변환만 가능하다. value타입은 불가능하다.

6.10.1 클래스 타입 형변환 기초

1. 기본 데이터형에서의 형변환

  • 형변환으로 사용된 double과 double& 차이점은 무엇인가?

i를 double 타입으로 형변환하는 것은 임시값을 만드는 것이다.

int형인 i를 double& 타입으로 변경하는 것은
i객체가 가리키는 주소를 8바이트의 메모리 덩어리인 double 객체로 변환하는 것이다. but, 실제 값 반환은 하지 않는다.

출력 결과

  • a-1 , b-1은 기본 c++문법이다.
  • a-1은 암묵적인 변환이다.
  • b-2가 오류가 나는 이유는 레퍼런스값인 d5는 lValue만 받을 수 있는데,
    (double)i 는 임시 객체이다.
  • b-1에서 오류가 발생하는 것은 암묵적으로 i가 차지하는 메모리영역을 단지 확장하여 double 로 만들려고 하는데, 위험해서 컴파일러가 오류를 발생시키는 것이다.
  • 출력결과에서 d3과 d6이 제대로된 값이 들어가 있지 않은 이유는
    레퍼런스 타입 형변환은 메모리가 반환되는 것일뿐, 실제값을 반환해주지는 않기 때문이다.

참조타입 변환은 메모리값만 반환되는 것이다!
value타입 변환은 임시값은 반환하는 것이다!

그럼에도 불구하고, a-3과 b-3이 에러 발생하지 않는 이유는
b-1은 암묵적 변환이어서 컴파일러가 사전에 오류를 발생하는 것이고,
a-3과 b-3은 명시적으로 변환연산자를 사용했기 때문이다.
단 그에 따른 책임은 순전히 유저가...

2. 클래스에서의 형변환

1.상관없는 클래스 간의 형변환-1

: 2개의 클래스 메모리 구조가 같으므로, 위에서 기본데이터 형변환 한것처럼
형변환이 이루어져야 하는데, 불가능하다.
왜냐하면, 어떤 기준에 의해서 변환을 해야 할지 알 수 없기 때문이다.

  • 복사생성자를 추가하면 에러 줄일수 있다.
    왜냐하면 형변환을 어떻게 할 것인지에 대한 기준을 제시했기 때문이다.
    물론 임시 객체가 생성될 것이다.

    -> 그런데도 reinter는 안되는데?

reinterpret_cast는 타입 형변환을 허용하지 않는다.

  • 참조로 형변환을 한다면 위 ##1번에서 확인한 것처럼. 메모리를 반환하기 때문에... 동일하게 되지 않을까????

    -> 동일한 값을 가진다.

번외 - 클래스 구조를 변경하면서 형변환을 해보자!

  • 현재 2개의 클래스가 동일한 메모리 구조를 가지고 있어서 그러는 것같은데?
    클래스 메모리 구조를 변경해보자!

    -> 클래스 메모리 구조를 공부할때 선언 순서에 따라서 메모리가 나열된다는 것을 확인했는데, 거기에 맞춰서 변환이 이루어진 것이다.

  • float형으로 바꾸면, 7이 float형으로 변경된 값이 들어온다...

알게 된점.

  • 서로 다른 클래스 간의 형변환에서는 메모리 구조가 동일해야만 형변환이 가능한 것이다.
  • 레퍼런스형 변환은 메모리 구조를 반환하는 것이다.

1. 상관 없는 클래스간 형변환-2

  • 가) 는 암묵적 타입 변환이지만, 컴파일러가 서로 상관없는 클래스를 캐치해서 오류를 발생시킨다.
  • 나) 는 강제적 타입 변환이다. 문제는 전적으로 유저 책임
  • 다) 는 static_cast는 서로 상속 관계에 있을때만 변환을 허용한다.
  • 라) 는 강제적 형변환이어서 가능한 것이다.

그러면 복사 생성자를 만들면 어떻게 될것인가?


: 변함없다. 왜냐하면 레퍼런스 타입 형변환은 원래 있던 객체의 메모리 영역 크기만 변경하여 반환하는 것이기 때문이다.

2. 상속 관계 클래스 타입 변환1

  • 자식에서 부모로 형변환하는 구간 : a,b,c,d
  • 부모에서 자식으로 형변환하는 구간 : e,f,g,h
  • a,b,c는 상식적으로 당연하다.
    자식에서는 이미 부모의 멤버값을 가지고 있기 때문에,
    부모로도 변환이 가능하다.
    즉 큰 범위에서 작은 범위로 낮아지는 것을 생각하면된다.

  • 하지만 d가 안되는 이유는 reinter~는 형변환을 허용하지 않기 때문이다.

  • 부모에서 자식으로의 형변환은 불가능하다.
    작은 범위에서 큰범위로 변환하기에는 자식의 멤버들이 뭐가 있는지 정보를 알수 없어서이다.

적절한 생성자를 정의한다면 부모에서 자식으로의 형변환이 가능하다!

2. 상속 관계 클래스 타입 변환2

: 참조를 이용한 타입 형변환은 메모리를 확장하거나, 축소한다.

  • e는 암묵적인 형변환이어서 컴파일러 자체가 오류를 발생!
  • f,g,h는 메모리를 확장시킨 것이다. CChild의 mChild의 값이 어떤 값이 들어올지는 미정이다! 하지만 유저가 타입변환 연산자를 사용했으므로, 책임은 니가 알아서해!

출력해보자!

2. 상속 관계 클래스 타입변환3

: 자식의 멤버값 유효하게 하기

3. 정리

value 타입 변환은 결과로 임시 객체가 생성되는 것이다.
reference 타입 변환은 객체의 메모리의 크기를 변경되는 것이다.
자식에서 부모로의 형변환은 상식적으로 가능하다.
부모에서 자식으로의 형변환은 값 타입에서는 금지되지만,
ref타입에서는 유저가 직접 형변환 할 경우에는 가능하지만, 책임은 유저가.
static_cast는 오로지 상속에서만 사용가능하다.
reinterpter_cast는 참조타입이나 포인터 타입으로의 변환만 가능하다.

6.10.3 클래스 포인터 타입 변환

: 포인터는 어쨋든 4바이트 혹은 8바이트의 메모리 블록이므로 문제 없을 것이라 생각하지만, 문제를 발생시킬 수 있다.

1. 상관 없는 클래스 사이의 포인터 변환

  • 포인터 타입 변환을 하게 되면 기존 객체가 차지하는 메모리 영역을 적절히 조정하여 주소를 반환하는 것이다.

  • a는 암묵적인 변환인데 컴파일러가 관련없는 클래스여서 거부하는 것이다.

  • b는 명시적으로 변환하는 것인데 내부 값 달라도 책임 안짐

  • c는 static_cast는 상속에서만 가능하다.

  • d는 강제적인 형변환이다. //포인터여서 가능하다.

    -> reinterpret_cast는 굉장히 무섭고 위험한 형변환 친구다...

2. 상속 관계에서의 포인터 변환

-> 부모에서 자식으로의 암묵적인 형변환은 올바르지 못하므로 컴파일러가 막고 있다.
상속에서의 형변환은 대체적으로 이루어진다. 하지만 하향형변환은 올바른지는
전적으로 유저의 책임이다.

  • 포인터형변환을 한다고 해서 메모리 주소가 달라지지는 않는다.

6.10.4 클래스 포인터 타입 변환에서 메모리 주소가 달라질 수 있다.

1. 다중 상속 관계의 메모리 구조.


-> 다중 상속할경우, 부모 클래스의 메모리들이 선언한 순서대로 메모리에 배치되어 있다.
CParentB로 형변환을 하고 있다.
클래스의 메모리 배치를 보면, CParentB가 시작하는 메모리 값이고,
CParentA가 int형만 큼 차이가 나므로 4바이트 차이가 발생한 것이다.

  • 멤버 변수 확인하기

    -> reinterpret 형변환의 멤버가 1 인 이유는 reinterpret은 클래스의 상속과 관계없이 포인터값을 유지하면서 타입변환을 하기 때문에 최상단 부모의 멤버를 출력하게 되는 것이다.

2. 단일 상속에서의 virtual 포함한 메모리 구조.

: 가상함수가 존재할 경우 클래스 객체의 메모리 시작 위치에 가상함수 포인터가 생성된다.

-> pC에서 부모의 멤버인 mValue보다도 위에 가상함수 포인터의 메모리가 자리잡는 것이다.

profile
🔥🔥🔥

0개의 댓글

관련 채용 정보