---
description: >-
  ex00과제에서 static_cast 를, ex01과제에서 reinterpret_cast 를, ex02과제에서 dynamic_cast 에
  대해 학습한다.
---

# \[CPP-06] CPP 형변환 연산자

## ex00: Scalar conversion

**\[과제 소개]**

1. 들어온 인자를 타입별(char, int, float, double)로 변환하여 출력하는 프로그램을 작성한다.
2. 받은 인자의 리터럴 타입을 감지할 수 있어야하며, 예시는 다음과 같다.
   * `char` literal values: ‘c’, ‘a’…
     * 인자에 논프린터블은 안들어오는 걸로 친다.
     * 만약 변환한 숫자가 논프린터블이면 결과 대신 알림을 넣다.
   * `int` literal values: 0, -42, 42…
   * `float` literal values: 0.0f, -4.2f, 4.2f… -inff, +inff, nanf
   * `double` literal values: 0.0, -4.2, 4.2… -inf, +inf, nan
3.  `inf`, `nan` 이란?

    `inf`는 무한대값을 의미한다.

    `nan`(Not A Number) 은 "숫자가 아님"을 의미하며, 그 결과를 숫자로 표현할 수 없는 계산을 수행할 때 얻게 되는 float value 이다. NaN을 사용하여 수행하는 모든 계산도 NaN으로 귀결된다.

```cpp
   >>> 2*float("inf")
   inf
   >>> -2*float("inf")
   -inf
   >>> float("inf")-float("inf")
   nan
```

1. 다음 예시처럼 출력한다. 변환이 불가능하거나 오버플로우는 "conversion is impossible" 이라고 출력한다.

```cpp
   /convert 0
   char: Non displayable
   int: 0
   float: 0.0f
   double: 0.0

   ./convert nan
   char: impossible
   int: impossible
   float: nanf
   double: nan

   ./convert 42.0f
   char: '*'
   int: 42
   float: 42.0f
   double: 42.0
```

1. 허용 함수
   * `stoi()`, `stof()`, `stod()` : 각각 string을 int, float, double로 변환한다.

### 1. CPP 형변환 연산자

형변환의 의도를 더 잘 표현하기 위해 상황에 맞는 4가지 형변환이 존재한다. 요구한 형 변환이 적절한 경우에는 형 변환된 데이터를 반환하지만, 요구한 형 변환이 적절하지 않은 경우 컴파일시 에러가 발생한다.

* `<Type>` : 변환하고자 하는 자료형. 객체의 `포인터` 또는 `참조형`이 와야한다.
* `(expression)` : 변환의 대상

#### 1) static\_cast

```cpp
static_cast<T>(expr)
```

* 기본 자료형 데이터간의 형변환에 사용한다.
* 암시적 형변환이 가능할 때 static\_cast로 명시적 형변환을 시켜줄 수 있다.
* 포인터 타입(`expr`)을 다른 것(`<T>`)으로 변환하는 것은 허용하지 않지만 상속 관계에 있는 포인터끼리 변환은 가능하다.&#x20;
  * 다만 다운캐스트 (`static_cast<기초클래스>(파생클래스)`)시에는 안전하지 않게 동작할 수 있기 때문에, 이 경우에는 dynamic\_cast를 사용한다.

#### 2) dynamic\_cast

```cpp
dynamic_cast<T>(expr)
```

```cpp
class Truck : public Car 일 때

Truck * ptruck = new Truck();
Car * pcar = dynamic_cast<Car*>(ptruck);
```

* 서로 상속 관계에 있는 클래스간의 형변환에 사용한다.
* 런타임 시간에 실제로 캐스팅이 가능한지 검사하기 때문에 안전한 다운캐스팅, 업캐스팅에 사용된다.&#x20;

#### 3) const\_cast

```cpp
const_cast<T>(expr)
```

```cpp
void test(char *str){}

int main(void)
{
    const char *name = "홍길동";
    test(const_cast<char *>(name); 
}
```

* 함수의 인자전달 시 const 선언으로 인한 type 불일치가 발생해서 인자의 전달이 불가능 경우 사용된다.
* 즉, 포인터 또는 참조형의 const를 잠깐 풀어주는데 사용한다. 그러나 일반 변수, 함수 포인터, 멤버 함수에 대한 const는 풀어줄 수 없다.
  * 원본은 변경하지 않기 때문에 안전하지만, 값을 포인터로 바꾼후 원본값을 새로운 포인터로 넘겨받는다면 바뛴 값이 넘어단다. 이 경우 조심.

#### 4) reinterpret\_cast

```cpp
reinterpret_cast<T>(expr)
```

* 임의의 포인터 타입간의 형변환에 사용된다.
  * 포인터 -> 포인터, 포인터 -> 일반변수, 일반변수 -> 포인터
* 정수형을 포인터로 바꿀 수도 있다.
* 즉, 일반적으로 허용하지 않는 위험한 형 변환을 (무조건적인 형 변환) 할 때 사용한다.
  * 만약 `int *` -> `char` -> `int *` 로 이뤄지는 형변환이 있을 때, 주소값이 파괴되어 결국 nullptr을 가르키게 된다. char형은 1바이트 크기여서 주소 값을 다 표현하지 못하기 때문이다. 즉, 데이터를 옮기는 과정에서 dump가 되버려 원본 데이터가 파괴될 위험이 있다.
* `(void *)`로도 전달이 가능하기 때문에 특수한 경우에 쓰인다.

## ex01 : Serialization (직렬화)

**\[과제 소개]**

* `void * serialize(void)` 함수를 작성하시오
  * 이 함수는 직렬화된 데이터 조각을 나타내는 바이트 스퀸스 힙의 주소를 반환합니다.
  * 직렬화 된 데이터는 \[8개의 영숫자 문자로 구성된 임의 배열, 임의의 정수, 8개 영숫자 문자로 구성된 두 번째 임의 배열]의 연결입니다.
* `Data * deserialize (void * raw)` 함수를 작성합니다.&#x20;
  * 이 함수는 struct Data {std::string s1; int n; std::string s2;}; 로 정의된 데이터 구조를 역 직렬화 합니다(힙에 할당)

### 1. std::string의 크기는 몇 byte?

```cpp
ptr = new char[sizeof(std::string) * 2 + sizeof(int)];
ptr = new char[52];
```

**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](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는 비교 연산을 할 수 있기 때문에 이 문제에서는 사용이 금지된다.

```cpp
typeid(int) == typeid(double) ;
```
