Skip to content

Commit

Permalink
docs: 📝 add post
Browse files Browse the repository at this point in the history
  • Loading branch information
nyj001012 committed Oct 17, 2023
1 parent 0b21f7a commit ab55007
Showing 1 changed file with 110 additions and 0 deletions.
110 changes: 110 additions & 0 deletions _posts/42_seoul/2023-10-17-cpp06.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: "CPP Module 06"
excerpt: "C++ 에서의 형변환
category:
- 42_seoul
author_profile: true
sidebar:
- nav: "main"
tag:
- 42서울
- CPP
toc: true
toc_sticky: true
last_modified_at: 2023-10-17T00:00:00+09:00
---

# CPP Module 06
## 과제를 통해 배울 수 있는 것
기존에 파이썬이나 자바를 쓰던 내 입장에서는 형변환 종류에 명시적 형변환, 그리고 암시적 형변환 정도만 알고 있었다. 그런데 C++ 에서는 단순히 그 두 가지 종류가 아닌, 여러 가지 종류의 형변환을 사용할 수 있었고 이번 과제는 그 다양한 종류의 형변환을 사용하는 것이다.

## Exercise 00: Conversion of scalar types
> 참고: [static_cast conversion: 공식 문서](https://en.cppreference.com/w/cpp/language/static_cast)
> 참고: [static_cast in C++: geeksforgeeks](https://www.geeksforgeeks.org/static_cast-in-cpp/)
단순히 말하면 `char`, `int`, `double`, `float`끼리의 스칼라 타입을 형변환하는 과제이다.
이들끼리는 **명시적**으로 형변환 해야한다고 되어있다. 기타 예외 처리 이런 건 제쳐두고 핵심 부분인 형변환만 다루도록 하겠다.

### static_cast
Ex00에서 사용할 형변환은 `static_cast` 이다.

`static_cast`는 암묵적 및 사용자가 정의한 형변환의 조합을 사용하여 타입 간 형변환을 수행한다... 고 나와있는데, 이게 무슨 소리냐면 그냥 `정수 -> 실수`, `포인터 -> (void *)`로 형변환 할 수 있다는 얘기다.

`static_cast`는 컴파일 단계에서 형변환이 이루어지기 때문에 컴파일러가 타입을 검증하여 타입 안정성이 보장된다는 장점이 있다. 그리고 당연히 명시적이기도 하다.
반대로 타입 안정성이 보장된다는 얘기는 타입에 있어서 유연하지 않다는 것이기도 하다.

사용 예시는 다음과 같다.

```C++
void ScalarConverter::strToChar() {
std::cout << "char: " << _str[0] << std::endl;
std::cout << "int: " << static_cast<int>(_str[0]) << std::endl;
std::cout << "float: " << static_cast<float>(_str[0]) << std::endl;
std::cout << "double: " << static_cast<double>(_str[0]) << std::endl;
}
```

`ScalarConverter` 클래스의 구현 일부분을 가져왔다. 멤버 변수 `_str`의 앞 글자를 `static_cast`를 이용하여 원하는 타입으로 형변환 해주는 식으로 구현했다.

## Exercise 01: Serialization
### 직렬화 하는 이유
> 참고: (데이터 직렬화(serialization)는 무엇이고 왜 필요한가?)[https://hub1234.tistory.com/26]
디스크에 저장하거나 통신할 때에는 값 형식 데이터만 가능하다. 이때 값 형식 데이터는 스택에 메모리가 쌓이고 직접 접근이 가능한 데이터(ex. int, float, char, ...)를 말한다. **참조 형식 데이터는 힙에 할당된 메모리 주소를 가지고 있어서 저장, 통신에 사용할 수 없다.**

생각해보면 당연한게, 내 컴퓨터에서 A라는 메모리 공간에 데이터를 할당했다 치면 그 A 주소 값을 다른 컴퓨터에 넘겨봤자 주소가 같을 리가 없다.

그래서 **각 주소 값이 가지는 데이터를 전부 모아 값 형식의 데이터로 변환**하는, 데이터 직렬화가 필요한 것이다.

### reinterpret_cast
> 참고: [reinterpret_cast conversion
: 공식 문서](https://en.cppreference.com/w/cpp/language/reinterpret_cast)
> 참고: [reinterpret_cast 연산자: Microsoft
](https://learn.microsoft.com/ko-kr/cpp/cpp/reinterpret-cast-operator?view=msvc-170)

(공식 문서 양이 엄청 방대하다...)

`reinterpret_cast``포인터 <-> 다른 포인터`, `정수 계열 <-> 포인터` 형식의 변환을 제공한다. 서브젝트에서 요구한 `Data * <-> uintptr_t` 형변환에 제격이다.

해당 내용을 아래와 같이 적용할 수 있다.

```C++
uintptr_t Serializer::serialize(Data *ptr) {
return reinterpret_cast<uintptr_t>(ptr);
}

Data *Serializer::deserialize(uintptr_t raw) {
return reinterpret_cast<Data *>(raw);
}
```
## Exercise 02: Identify real type
### dynamic_cast
> 참고: [dynamic_cast conversion
: 공식 문서](https://en.cppreference.com/w/cpp/language/dynamic_cast)
> 참고: [dynamic_cast 연산자
: Microsoft](https://learn.microsoft.com/ko-kr/cpp/cpp/dynamic-cast-operator?view=msvc-170)
이번 과제는 어떤 객체가 주어질 때, 이 객체의 클래스 타입을 알아내는 내용이다. 이를 위해 `dynamic_cast` 형변환을 사용했다.
`dynamic_cast`는 클래스를 가리키는 포인터와 레퍼런스를 부모, 자식, 그리고 형제로 형변환한다. `Base` 클래스를 상속 받는 `A`, `B`, `C` 클래스가 있다면, `A`, `B`, `C` 끼리는 형제고, 각각 `Base`와는 부모-자식 관계니까 이 `dynamic_cast`를 사용하면 된다.
형변환에 실패하면 애초에 그 객체는 형변환 하려는 타입의 객체가 아니었던 거니까, 그 부분을 이용해서 아래와 같이 구현하면 객체의 타입을 알 수 있다.
```C++
void identify(Base *p) {
std::cout << "------------------- IDENTIFY *p -------------------" << std::endl;
if (dynamic_cast<A *>(p))
std::cout << "type: A" << std::endl;
if (dynamic_cast<B *>(p))
std::cout << "type: B" << std::endl;
if (dynamic_cast<C *>(p))
std::cout << "type: C" << std::endl;
}
```

## 번외. C++에서 정적 클래스를 만드는 방법
처음에는 단순히 다른 곳에서 했던 것처럼 `static class ScalarConverter` 이런 식으로 선언했는데, clion에서 그렇게 쓰는 거 아니라고 알려줬다.

슬랙을 조금 뒤져보니까 정적 멤버 변수와 정적 멤버 함수만 가지는 클래스인데, 와중에 생성자들은 객체를 만들 필요가 없기 때문에 private으로 선언한다고 한다.
맨날 `Car car = new Car("sonata", "차량 번호");` 이런 식으로 쓰다보니 저런 방식이 새롭긴 하다.

0 comments on commit ab55007

Please sign in to comment.