malloc을 사용할 때 void*을 반환하고, 이를 우리가 타입 변환을 통해 사용했었다.
타입 변환 유형(비트열 재구성 여부)
[1] 값 타입 변환
의미를 유지하기 위해서, 원본 객체와 다른 비트열을 재구성하는 것
int a = 123456789; // 2의보수
float b =(float)a; // 부동소수점(지수 + 유효숫자)
둘의 메모리를 확인해보면 둘이 아예 다르다는 것을 확인 할 수 있다. int를 float를 변환한것을 보면 데이터의 값은 얼추 비슷하지만(정수를 실수로 바꾸다보면 누락될 수 있음) 둘의 비트 저장 방식이 다르다 보니 비트열을 아예 바꿔 재구성한 것이다.
[2] 참조 타입 변환
비트열을 재구성하지 않고, 비트를 분석하는 관점만 바꾸는 것이다.
int a = 123456789; // 2의보수
float b =(float&)a; // 부동소수점(지수 + 유효숫자)
이렇게 하면 둘의 메모리를 보면 똑같은 걸 확인 할 수 있다. 하지만 값은 아예 달라지게 된다.
그럼 왜 쓰는가?
거의 쓸일은 없지만, 포인터 타입 변환도 참조 타입 변환과 동일한 룰을 따르니까 알아두자.
타입 변환 유형( 안전도 분류)
[1] 안전한 변환
의미가 항상 100% 완전한 일치하는 경우
같은 타입이면서 크기만 더 큰 바구니로 이동하는 경우
작은 바구니 -> 큰 바구니로 이동(업캐스팅)은 문제 없음
ex) char -> short, short->int, int->__int64
int a = 123456789;
__int64 b = a;
값이 변하지도, 메모리가 변하지도 않는다.
[2] 불안전한 변환
의미가 항상 100% 일치한다고 보장하지 못하는 경우
타입이 다르거나 같은 타입이지만 큰 바구니에서 작은 바구니로 이동하는 경우(다운캐스팅)
int a = 123456789;
float b = a;
short c = a;
이렇게 할 경우 a의 앞 2 바이트가 손실나게 된다.
타입 변환 유형(프로그래머 의도에 따라 분류)
[1] 암시적 변환
이미 알려진 타입 변환 규칙에 따라서 컴파일러가 '자동'으로 타입 변환
int a= 123456789;
float b = a //암시적으로
딱히 문제가 없지만 warning이 나온다. 데이터가 손실될 수 있다고 출력된다.
[2] 명시적 변환
프로그래머가 직접 어떤 타입으로 변환하겠다고 지시를 내려 변환
int a = 123456789;
int* b = (int*)a //명시적
타입 변환 유형(아무런 연관 관계가 없는 클래스 사이의 반환)
[1] 연관없는 클래스 사이의 값 타입 변환
#include <iostream>
using namespace std;
class Knight
{
public:
int _hp= 10;
};
class Dog
{
public:
int _age = 1;
int _cuteness = 2;
};
int main()
{
Knight knight;
Dog dog = (Dog)knight;
return 0;
}
일반적으로는 타입이 변환되지 않는다.
#include <iostream>
using namespace std;
class Knight
{
public:
int _hp= 10;
};
class Dog
{
public :
// 타입 변환 생성자
Dog(const Knight& knight)
{
_age = knight._hp;
}
operator Knight()
{
return (Knight)(*this);
}
public:
int _age = 1;
int _cuteness = 2;
};
int main()
{
Knight knight;
Dog dog = (Dog)knight;
Knight knight2 = dog;
return 0;
}
문법적으로 타입 변환 생성자 및 연산자를 지정해주면 가능은 하다.
[2] 연관 없는 클래스 사이의 참조 타입 변환
Knight knight;
Dog& dog = (Dog&)knight;
문법적으로 에러는 없지만, 메모리를 잘못 건드리면 엄청난 에러가 날 수 있다.
[3] 상속 관계에 있는 클래스 사이의 값 타입 변환
불독은 개인데 개는 불독이라고 말할 수없기 때문에 변환을 잘 해야한다.
#include <iostream>
using namespace std;
class Dog
{
public :
public:
int _age = 1;
int _cuteness = 2;
};
class BullDog : public Dog
{
public:
bool _french;
};
int main()
{
// 상속 관계 클래스의 값 타입 변환
Dog dog;
BullDog bulldog = (Bulldog)dog; //에러
BullDog bulldog2;
Dog dog2 = bulldog2; //가능
return 0;
}
자식에서 부모는 가능하지만 부모에서 자식은 안된다.
[4] 상속 관계에 있는 클래스 사이의 참조 타입 변환
Dog dog;
BullDog& bullDog = (BullDog&)dog;
BullDog bullDog2;
Dog& dog = bullDog2;
'C++ > 기초' 카테고리의 다른 글
[C++] 복사, cast (0) | 2023.11.28 |
---|---|
[C++] 타입 변환 - 2 (1) | 2023.11.28 |
[C++] 동적 할당 (1) | 2023.11.28 |
[C++] 객체 지향 - 3 (0) | 2023.11.27 |
[C++] 객체 지향 - 2 (1) | 2023.11.27 |