[CPP-06] CPP 형변환 연산자
ex00과제에서 static_cast 를, ex01과제에서 reinterpret_cast 를, ex02과제에서 dynamic_cast 에 대해 학습한다.
ex00: Scalar conversion
[과제 소개]
들어온 인자를 타입별(char, int, float, double)로 변환하여 출력하는 프로그램을 작성한다.
받은 인자의 리터럴 타입을 감지할 수 있어야하며, 예시는 다음과 같다.
char
literal values: ‘c’, ‘a’…인자에 논프린터블은 안들어오는 걸로 친다.
만약 변환한 숫자가 논프린터블이면 결과 대신 알림을 넣다.
int
literal values: 0, -42, 42…float
literal values: 0.0f, -4.2f, 4.2f… -inff, +inff, nanfdouble
literal values: 0.0, -4.2, 4.2… -inf, +inf, nan
inf
,nan
이란?inf
는 무한대값을 의미한다.nan
(Not A Number) 은 "숫자가 아님"을 의미하며, 그 결과를 숫자로 표현할 수 없는 계산을 수행할 때 얻게 되는 float value 이다. NaN을 사용하여 수행하는 모든 계산도 NaN으로 귀결된다.
다음 예시처럼 출력한다. 변환이 불가능하거나 오버플로우는 "conversion is impossible" 이라고 출력한다.
허용 함수
stoi()
,stof()
,stod()
: 각각 string을 int, float, double로 변환한다.
1. CPP 형변환 연산자
형변환의 의도를 더 잘 표현하기 위해 상황에 맞는 4가지 형변환이 존재한다. 요구한 형 변환이 적절한 경우에는 형 변환된 데이터를 반환하지만, 요구한 형 변환이 적절하지 않은 경우 컴파일시 에러가 발생한다.
<Type>
: 변환하고자 하는 자료형. 객체의포인터
또는참조형
이 와야한다.(expression)
: 변환의 대상
1) static_cast
기본 자료형 데이터간의 형변환에 사용한다.
암시적 형변환이 가능할 때 static_cast로 명시적 형변환을 시켜줄 수 있다.
포인터 타입(
expr
)을 다른 것(<T>
)으로 변환하는 것은 허용하지 않지만 상속 관계에 있는 포인터끼리 변환은 가능하다.다만 다운캐스트 (
static_cast<기초클래스>(파생클래스)
)시에는 안전하지 않게 동작할 수 있기 때문에, 이 경우에는 dynamic_cast를 사용한다.
2) dynamic_cast
서로 상속 관계에 있는 클래스간의 형변환에 사용한다.
런타임 시간에 실제로 캐스팅이 가능한지 검사하기 때문에 안전한 다운캐스팅, 업캐스팅에 사용된다.
3) const_cast
함수의 인자전달 시 const 선언으로 인한 type 불일치가 발생해서 인자의 전달이 불가능 경우 사용된다.
즉, 포인터 또는 참조형의 const를 잠깐 풀어주는데 사용한다. 그러나 일반 변수, 함수 포인터, 멤버 함수에 대한 const는 풀어줄 수 없다.
원본은 변경하지 않기 때문에 안전하지만, 값을 포인터로 바꾼후 원본값을 새로운 포인터로 넘겨받는다면 바뛴 값이 넘어단다. 이 경우 조심.
4) reinterpret_cast
임의의 포인터 타입간의 형변환에 사용된다.
포인터 -> 포인터, 포인터 -> 일반변수, 일반변수 -> 포인터
정수형을 포인터로 바꿀 수도 있다.
즉, 일반적으로 허용하지 않는 위험한 형 변환을 (무조건적인 형 변환) 할 때 사용한다.
만약
int *
->char
->int *
로 이뤄지는 형변환이 있을 때, 주소값이 파괴되어 결국 nullptr을 가르키게 된다. char형은 1바이트 크기여서 주소 값을 다 표현하지 못하기 때문이다. 즉, 데이터를 옮기는 과정에서 dump가 되버려 원본 데이터가 파괴될 위험이 있다.
(void *)
로도 전달이 가능하기 때문에 특수한 경우에 쓰인다.
ex01 : Serialization (직렬화)
[과제 소개]
void * serialize(void)
함수를 작성하시오이 함수는 직렬화된 데이터 조각을 나타내는 바이트 스퀸스 힙의 주소를 반환합니다.
직렬화 된 데이터는 [8개의 영숫자 문자로 구성된 임의 배열, 임의의 정수, 8개 영숫자 문자로 구성된 두 번째 임의 배열]의 연결입니다.
Data * deserialize (void * raw)
함수를 작성합니다.이 함수는 struct Data {std::string s1; int n; std::string s2;}; 로 정의된 데이터 구조를 역 직렬화 합니다(힙에 할당)
1. std::string의 크기는 몇 byte?
sizeof(std::string) 는 왜 24byte인가?
컴퓨터 시스템(window, linux, mac, compiler)마다 다르겠지만 libc++기준 문자열 클래스의 한 크기는 24byte이다. 그런데 std::string str = "hello"
일 때 str.size()
or str.length()
는 5가 나온다.
이는 문자열 클래스 24byte 안에 hello 외에 다른 값들이 존재한다는 것을 의미한다. 예를 들면 hello
가리키는 포인터가 존재한다. 24byte 안에 실제 문자열이 들어있는 것이 아닌 멤버 변수들(실제 문자열을 가르키는 포인터, 패딩 및 기타 ..)이 모여 24byte의 크기를 갖게 되는 것이다.
2. 직렬화
구조가 있는 객체의 내용물
을 바이트 배열로 저장하는 것을 직렬화(Serialization)이라고 하고, 반대로 바이트 배열로부터 내용물을 읽어와 객체에 채우는 것을 역직렬화(Deserialization)라고 합니다.
메모리안의 어떠한 (추상적) 데이터구조를 연속된 bit 로 외부에 보내기위해 정렬하는것.
그렇다면 이 문제에서는 string, int, string 구조체의 직렬화를 위해 몇 바이트 배열을 만들어야되냐가 문제인데, 총 52바이트(24 + 8 + 24) 인 것이다.
3. void* 형변환
https://dojang.io/mod/page/view.php?id=495
reinterpret_cast 를 사용한다.
ex02: Identify real type
[과제 소개]
가상 소멸자만 소유하는 Base 클래스 만드시오.
A,B,C는 빈 클래스 상속받음
Base * generate (void)
함수 작성하시오 : A,B or C 무작위 인스턴스, 인스턴스 Base 포인터 반환void identify_from_pointer(Base *p) 함수 작성 : p의 type에 따라 A,B,C 표시
void identify_from_reference(Base & p) : p의 타입에 따라 A,B,C 표시
<typeinfo>
사용금지down cast : 상위클래스의 포인터및 참조형 데이터를 하위클래스의 포인터 및 참조형 데이터로 변환
down cast를 할때 expr가 가리키고있는 객체 포인터 혹은 레퍼런스의 자료형이
<T>
와 다르면 NULL이 반환됨 -> 이를 이용해 안전하게 다운캐스트 가능.typeid는 비교 연산을 할 수 있기 때문에 이 문제에서는 사용이 금지된다.
Last updated