Search

CPP Module 06

Created
2021/10/13
tag
42μ„œμšΈ
42Seoul
CPP Module
All of C++98
Type Conversion
Static Casting
Reinterpret Casting
Dynamic Casting
RTTI
Serialization / Deserialization

Subjects

1. ex00 (Scalar conversion)

42.0f와 같이 μ†Œμˆ˜μ  뒀에 λ¦¬ν„°λŸ΄ κ°’μ˜ ν‘œκΈ°κ°€ μžˆμ„ λ•Œ μ²˜λ¦¬κ°€ μ•ˆ λ˜μ—ˆλ˜ κΈ°μ‘΄ 방법 (slee2 λ‹˜ μ˜ˆμ™Έ μΌ€μ΄μŠ€ λ‹€μˆ˜ 발견 κ°μ‚¬ν•©λ‹ˆλ‹€)

std::strtod

ν† κΈ€ 처리된 κΈ€μ˜ 타이틀을 보면 μ•Œ 수 μžˆλ“―μ΄, μˆ˜μ • 이전 λ°©λ²•μ—λŠ” 1..1...κ³Ό 같은 μΌ€μ΄μŠ€λ₯Ό ν¬ν•¨ν•˜μ—¬ μ†Œμˆ˜μ  μ•„λž˜μ˜ λ¦¬ν„°λŸ΄ κ°’μ˜ ν‘œκΈ°κ°€ μžˆλŠ” 경우 정상적인 μ²˜λ¦¬κ°€ λ˜μ§€ μ•Šμ•˜λ‹€. 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ std::atof ν˜Ήμ€ std::strtodλ₯Ό μ΄μš©ν–ˆλ‹€.
μŠ€νŠΈλ¦Όμ„ μ΄μš©ν•œ λ³€ν™˜μ˜ ν•œκ³„μ μ„ λͺ…ν™•νžˆ λ³Ό 수 있고, μ™œ C++11 μ΄ν›„μ—λŠ” 슀트림 λ³€ν™˜ λ³΄λ‹€λŠ” std::stoi, std::stof, std::stod 등을 μ΄μš©ν•˜λŠ” 것이 ꢌμž₯λ˜λŠ”μ§€ μ•Œ 수 μžˆλ‹€.
std::atofλŠ” 이름과 달리 const char*λ₯Ό 숫자둜 λ³€ν™˜ν•  λ•Œ double νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜κ²Œ λœλ‹€. std::strtod의 역할도 std::atof와 λ™μΌν•˜κ²Œ const char*λ₯Ό double νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜λŠ”λ°, κΈ°λŠ₯을 비ꡐ ν–ˆμ„ λ•Œ std::atof보닀 μƒλŒ€μ μœΌλ‘œ 더 νƒ„νƒ„ν•˜λ‹€. 예λ₯Ό λ“€μ–΄ std::atofλ₯Ό μ΄μš©ν–ˆμ„ λ•Œ, λ³€ν™˜ν•˜λ €λŠ” 값이 double의 ν‘œκΈ° λ²”μœ„λ₯Ό λ„˜μ–΄κ°€λŠ” κ²½μš°μ—λŠ” Undefined Behavior이닀. 반면 std::strtodλŠ” 이와 같은 κ²½μš°μ—μ„œ HUGE_VAL이 λ°˜ν™˜λ˜μ–΄ μ •μƒμ μœΌλ‘œ μ΄μš©λ˜λ„λ‘ κ΅¬ν˜„λ˜μ–΄ 있고 (errnoλŠ” ERANGE둜 μ„€μ •), 특히 std::atof와 달리 char** 인자λ₯Ό ν•˜λ‚˜ 더 받도둝 λ˜μ–΄ μžˆμ–΄μ„œ 숫자둜 λ³€ν™˜λ˜μ§€ μ•ŠλŠ” μœ„μΉ˜λ₯Ό μ„€μ •ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€.
try { char *ptr = NULL; *(const_cast<double*>(&_value)) = std::strtod(_input.c_str(), &ptr); if (_value == 0.0 && (_input[0] != '-' && _input[0] != '+' && !std::isdigit(_input[0]))) throw (std::bad_alloc()); if (*ptr && std::strcmp(ptr, "f")) throw (std::bad_alloc()); } catch (std::exception&) { _e = true; }
C++
λ”°λΌμ„œ std::strtodλ₯Ό μ΄μš©ν•˜μ—¬ double νƒ€μž…μœΌλ‘œ 값을 λ³€ν™˜ν•  λ•Œ μœ„μΉ˜ ν‘œκΈ°μš© char*의 μ£Όμ†Œκ°’μ„ λ„˜κ²¨μ„œ λ³€ν™˜ν•˜λ©΄ λ¦¬ν„°λŸ΄ κ°’μ˜ ν‘œκΈ° 및 μ›ν•˜μ§€ μ•ŠλŠ” 값에 λŒ€ν•œ 검증이 κ°€λŠ₯ν•˜λ‹€. ν•΄λ‹Ή μ£Όμ†Œλ₯Ό μ—­μ°Έμ‘° ν–ˆμ„ λ•Œ 정상 λ³€ν™˜μ΄λΌλ©΄ '\0'을 얻을 수 있고 그렇지 μ•ŠμœΌλ©΄ '\0'μ™Έμ˜ λ‹€λ₯Έ 값을 μ–»κ²Œ 될 것이닀. 특히 ν›„μžμ˜ κ²½μš°μ— μˆœμˆ˜ν•˜κ²Œ f둜 λ‚˜νƒ€λ‚˜μ§€ μ•ŠλŠ”λ‹€λ©΄, λ³€ν™˜μ΄ 정상적이지 μ•Šλ‹€λŠ” 처리λ₯Ό ν•΄μ£Όλ©΄ λ˜κ² λ‹€. λ‚΄ κ²½μš°μ—λŠ” std::bad_alloc을 λ˜μ Έμ„œ Exception을 λ§ˆν‚Ήν•˜κ³ , 좜λ ₯ μ‹œμ— λ§ˆν‚Ή 값을 ν™œμš©ν•˜λ„λ‘ λ§Œλ“€μ—ˆλ‹€.
정상 λ³€ν™˜μΈ κ²½μš°μ—” 숫자 μ΄ν›„μ˜ 값은 λ¬Έμžμ—΄μ˜ κ°€μž₯ 끝 값인 '\0'μ΄λ―€λ‘œ char*λ₯Ό μ—­μ°Έμ‘°ν•˜μ—¬ κ·Έ 값을 μ–»μ–΄λ‚Ό 수 μžˆλ‹€.

std::isnan & std::isinf

μ •μƒμ μœΌλ‘œ double 값을 μΆ”μΆœν–ˆλ‹€λ©΄ char, int, float, double둜 ν˜• λ³€ν™˜μ΄ κ°€λŠ₯ν•œλ°, λ¬Έμ œμ—μ„œ μš”κ΅¬ν•˜λŠ” 좜λ ₯을 μœ„ν•΄μ„  κ·Έ 값이 nan인지 inf인지도 ꡬ뢄할 ν•„μš”κ°€ μžˆλ‹€. μ΄λŠ” <cmath>의 std::isnan, std::isinfλ₯Ό μ΄μš©ν•˜μ—¬ μ•Œμ•„λ‚Ό 수 μžˆλ‹€.
cpprefence ν˜Ήμ€ cplusplusμ—μ„œ 검색해보면 두 ν•¨μˆ˜κ°€ C++11인 것을 λ³Ό 수 μžˆλŠ”λ°, C99 λ²„μ „μ˜ isnanκ³Ό isinfκ°€ <cmath>에도 μ‘΄μž¬ν•©λ‹ˆλ‹€. 두 ν•¨μˆ˜λŠ” λͺ¨λ‘ 맀크둜 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” ν•¨μˆ˜μ΄κ³ , C99 λ²„μ „μ˜ <cmath>λŠ” C++98에 μ™„μ „ν•œ ν˜Έν™˜μ„±μ„ κ°€μ§‘λ‹ˆλ‹€. μ•„λž˜ 링크듀을 톡해 isnan, isinf에 λŒ€ν•œ 정보 그리고 ν˜Έν™˜μ„±μ˜ 레퍼런슀λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 어렴풋이 κ²½ν—˜μ μœΌλ‘œλ§Œ μ•Œκ³  있던 κ²ƒμ΄μ—ˆλŠ”λ°, μ‹€μ œ 레퍼런슀 체크λ₯Ό ν•  수 μžˆλ„λ‘ 도와주신 gshimλ‹˜κ»˜ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€.

std::showpos & std::numeric_limits<T>::digits10

λ¬Έμ œμ—μ„œ inf에 λŒ€ν•΄μ„œ λͺ…ν™•ν•œ λΆ€ν˜Έλ₯Ό 뽑도둝 λ˜μ–΄ μžˆλŠ”λ°, μ΄λŠ” <iomanip>의 std::showposλ₯Ό std::cout으둜 좜λ ₯ν•˜λ €λŠ” κ°’ 이전에 λ„£κ³  좜λ ₯ν•˜λ©΄ +, - λΆ€ν˜Έλ₯Ό λ³Ό 수 μžˆλ‹€. std::cout의 κΈ°λ³Έ 섀정은 std::noshowposμ΄λ―€λ‘œ λ³„λ„μ˜ 섀정이 λ°˜λ“œμ‹œ ν•„μš”ν•˜λ‹€.
κ°„ν˜Ή 맀우 큰 μˆ˜μ— 맀우 κΈ΄ μ†Œμˆ˜μ μ„ λ„£μœΌλ©΄ κ·Έ 값이 μž˜λ €μ„œ ν‘œν˜„λ˜λŠ” κ²½μš°κ°€ μžˆλŠ”λ°, μ΄λŠ” std::stringstream으둜 값을 잘λͺ» μ°Ύμ•„λ‚Έ 것이 μ•„λ‹ˆλΌ λ‹¨μˆœνžˆ std::cout의 좜λ ₯에 λŒ€ν•œ precision μ„€μ • λ•Œλ¬Έμ— 그런 것이닀. λ”°λΌμ„œ std::setprecision ν•¨μˆ˜λ₯Ό std::cout으둜 좜λ ₯ν•˜λ €λŠ” κ°’ 이전에 λ„£κ³  좜λ ₯ν•˜λ©΄, 정해진 precision으둜 값을 좜λ ₯ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€. 단, μ„€μ •λœ precision이 ν‘œν˜„ν•  수 μžˆλŠ” precision보닀 λ†’λ‹€λ©΄, 원본 κ°’μ˜ precision을 μžƒμœΌλ©΄μ„œ μ„€μ •λœ precision을 μ΅œλŒ€ν•œ λ§žμΆ°μ„œ ν‘œν˜„ν•˜λ €κ³  ν•œλ‹€. 이 λ•Œλ¬Έμ— precision을 μžƒμ§€ μ•Šκ²Œ ν‘œν˜„ν•  수 μžˆλŠ” μ΅œλŒ€μ˜ precision을 μœ μ§€ν•˜λ©΄μ„œ 값을 좜λ ₯ν•  수 μžˆλ„λ‘, std::coutκ³Ό 좜λ ₯ν•˜λ €λŠ” κ°’ 사이에 std::numeric_limits<T>::digits10을 μž‘μ„±ν–ˆλ‹€.
std::numeric_limits<T>λŠ” <limits>μ—μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.

2. ex01 (Serialization)

Only Semantic !

Serializationμ΄λΌλŠ” μš©μ–΄ λ•Œλ¬Έμ— 많이 ν—·κ°ˆλ¦¬λŠ” 것 같은데, 문제λ₯Ό λ‹¨μˆœν™”ν•΄μ„œ λ³Ό ν•„μš”κ°€ μžˆλ‹€. 이 λ¬Έμ œμ—μ„œ μš”κ΅¬ν•˜λŠ” 것은 μ‚¬μš©μžκ°€ 직접 μ •μ˜ν•œ μž„μ˜μ˜ 클래슀 ν˜Ήμ€ ꡬ쑰체인 Data의 포인터λ₯Ό uintptr_t둜 λ³€ν™˜ν•˜κ³ , uintptr_tλ₯Ό λ‹€μ‹œ Data의 ν¬μΈν„°λ‘œ λ³€ν™˜ν•˜λŠ” 것이닀. 이 κ³Όμ •μ—μ„œ λˆ„λ½λ˜λŠ” 데이터가 μ—†λŠ”μ§€ 확인해야 ν•˜κ³ , 데이터듀은 μ˜¨μ „νžˆ μœ μ§€λ˜μ–΄μ•Ό ν•œλ‹€.
이와 같은 Aβ†’B, Bβ†’A의 λ³€ν™˜μ˜ μ˜λ―Έκ°€ 곧 Serialization의 ν•΅μ‹¬μ΄λ―€λ‘œ, ex01의 이름이 Serialization이라고 보면 λœλ‹€.

uintptr_t

uintptr_tλŠ” λΆ€ν˜Έ μ—†λŠ” 숫자 νƒ€μž…μ˜ 별칭이닀. λ‚΄ κ²½μš°μ—λŠ” uintptr_tλŠ” unsigned long으둜 λ˜μ–΄ μžˆλ‹€. λΆ€ν˜Έ μžˆλŠ” 숫자 νƒ€μž…μ˜ λ³„μΉ­μœΌλ‘  intptr_tκ°€ 있으며, uintptr_t와 intptr_tλŠ” 포인터가 μ°Έμ‘°ν•˜λŠ” μ£Όμ†Œλ₯Ό 숫자둜 μ €μž₯ν•˜κΈ° μœ„ν•΄ μ΄μš©λœλ‹€λŠ” 점이 λ™μΌν•˜λ‹€. 일뢀 μ‹œμŠ€ν…œμ—μ„œλŠ” intptr_tκ°€ signed, unsigned의 할당이 λͺ¨λ‘ κ°€λŠ₯ν•œ 것에 λΉ„ν•΄, uintptr_t은 unsigned의 ν• λ‹Ήλ§Œ κ°€λŠ₯ν•˜κ³  signed에 λŒ€ν•΄μ„œ λ³„λ„μ˜ νƒ€μž… λ³€ν™˜μ΄ ν•„μš”ν•œ 것을 λ³Ό 수 μžˆλ‹€. 이 λ•Œλ¬Έμ— ν”„λ‘œκ·Έλž¨μ˜ μœ μ—°μ„±μ„ μœ„ν•΄ uintptr_tλ³΄λ‹€λŠ” intptr_tλ₯Ό μ΄μš©ν•˜λŠ” 것이 C μ–Έμ–΄ λ•ŒλΆ€ν„° ꢌμž₯λ˜μ–΄ μ™”λ‹€.
ex01은 uintptr_t의 μœ μ—°μ„± 문제의 상징성 λ•Œλ¬Έμ— intptr_tκ°€ μ•„λ‹Œ uintptr_tλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•˜λ‚˜ μ‘°μ‹¬μŠ€λ ˆ μΆ”μΈ‘ν•΄λ³Έλ‹€.
#include <cstdint> #include <iostream> int main(void) { int n1; unsigned int n2; uintptr_t p; p = reinterpret_cast<uintptr_t>(&n1); // p = &n1 -> error std::cout << &n1 << std::endl; std::cout << std::hex << p << std::endl; p = reinterpret_cast<uintptr_t>(&n2); // p = &n2 -> error std::cout << &n2 << std::endl; std::cout << std::hex << p << std::endl; return (0); }
C++
ν•˜μ§€λ§Œ C++μ—μ„œλŠ” 포인터에 λŒ€ν•œ (포인터 β†’ 포인터, κ°’ β†’ 포인터, 포인터 β†’ κ°’) λ³€ν™˜μ΄ ν•„μš”ν•œ κ²½μš°μ—λŠ” λͺ¨λ‘ λ³„λ„μ˜ ν˜• λ³€ν™˜μ„ λͺ…μ‹œν•΄μ€˜μ•Ό μ΄μš©ν•  수 μžˆλ‹€. 포인터에 λŒ€ν•œ ν˜• λ³€ν™˜μ€ reinterpret_cast<T>둜 μœ λ„ν•  수 있으며, κ·Έ μ‚¬μš© 방법은 μœ„μ™€ κ°™λ‹€. uintptr_t둜 좜λ ₯된 값을 std::hexλ₯Ό μ΄μš©ν•˜μ—¬ 16 μ§„μˆ˜λ‘œ 뽑아보면, μ›λž˜ 각 값이 μ €μž₯된 μ£Όμ†Œμ™€ λ™μΌν•œ 것을 확인할 수 μžˆλ‹€.
λ”°λΌμ„œ 이λ₯Ό 적절히 ν™œμš©ν•˜μ—¬ Data* β†’ uintptr_t 그리고 uintptr_t β†’ Data *λ₯Ό Serialize, Deserialize둜 λ§Œλ“€μ–΄λ³΄κ³ , 두 ν•¨μˆ˜μ˜ μˆ˜ν–‰ κ²°κ³Όκ°€ 초기 μƒνƒœμ˜ Data와 λ™μΌν•œμ§€ ν™•μΈν•˜λ©΄ λœλ‹€.

Serialization in Real

μ•„λ§ˆ 이 과제λ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ Serialization의 μ˜λ―Έκ°€ 크게 와닿지 μ•ŠλŠ” μ‚¬λžŒλ“€λ„ μžˆμ„ 것이닀. λ”°λΌμ„œ 이 뢀뢄을 κ°„λ‹¨ν•˜κ²Œ 짚고 λ„˜μ–΄κ°€λ³΄λ € ν•œλ‹€.
Serialization은 일반적으둜 4κ³„μΈ΅μ˜ TCP, UDP와 크게 관련이 있으며, 특히 Socket 톡신과 큰 관련이 μžˆλ‹€. 기본적으둜 TCP, UDP 톡신은 Process-to-Prcoess의 κ΄€κ³„μ—μ„œ 이뀄진닀. 3κ³„μΈ΅κΉŒμ§€ 거쳐 End-to-End의 ꡬ뢄을 톡해 μ–΄λŠ Machine으둜 κ°€μ•Όν•˜λŠ”μ§€ νŒλ³„μ΄ λ˜μ–΄ μžμ‹ μ—κ²Œ ν•΄λ‹Ήν•˜λŠ” Packet을 λͺ¨λ‘ λ°›κ³  λ‚˜λ©΄, 이λ₯Ό 적절히 μžμ‹ μ˜ Processλ“€μ—κ²Œ λ‚˜λˆ μ€˜μ•Ό ν•˜κ³  이것이 4κ³„μΈ΅μ˜ 주된 역할이닀. Process에 λŒ€ν•œ ꡬ뢄은 OS λ‹¨μœ„μ—μ„œ Portλ₯Ό μ΄μš©ν•˜μ—¬ 이뀄지고, 각 Process듀이 λ„˜κΈ°λ €λŠ” 데이터듀은 Port에 μ’…μ†λœ Socket Buffer에 κΈ°λ‘ν•˜μ—¬ OSκ°€ 적절히 μ²˜λ¦¬ν•˜μ—¬ λ‹€λ₯Έ Process ν˜Ήμ€ λ‹€λ₯Έ Machine으둜 λ³΄λ‚΄κ²Œ λœλ‹€. (이λ₯Ό OS Delegation이라 ν•œλ‹€.) 이 λ•Œ, Socket Buffer에 κΈ°λ‘ν•˜λ €λŠ” λ‚΄μš© 쀑에 ν¬μΈν„°λ‘œ μ°Έμ‘°ν•˜κ³  μžˆλŠ” μ£Όμ†Œκ°€ κΈ°λ‘λœλ‹€λ©΄ μ–΄λ–»κ²Œ 될까? λ‹€λ₯Έ Machine은 ν˜„μž¬ μ‚¬μš©μžμ˜ λ©”λͺ¨λ¦¬ μ£Όμ†Œμ— λŒ€ν•΄ μ•Œ 길이 μ—†κΈ° λ•Œλ¬Έμ—, ν•΄λ‹Ή λ°μ΄ν„°λŠ” 해석이 λΆˆκ°€λŠ₯ν•˜κ²Œ λœλ‹€. 운이 μ’‹μ•„μ„œ 동일 Machine의 λ‹€λ₯Έ Process둜 κ°„λ‹€κ³  해도, μ΄λŠ” 재배치 μ£Όμ†Œμ΄λ―€λ‘œ λ‹€λ₯Έ Processμ—μ„œλŠ” 이λ₯Ό μ•Œμ•„λ³΄κΈ°κ°€ νž˜λ“€λ‹€. λ”°λΌμ„œ 포인터에 λŒ€ν•΄μ„  λ³„λ„μ˜ λ³€ν™˜μ΄ ν•„μš”ν•˜λ‹€. 이λ₯Ό Serialization이라 ν•œλ‹€.
μœ„μ—μ„œ μš”κ΅¬λ˜λŠ” κΈ°λ³Έ κ°œλ…λ“€μ€ netwhat에 μ •λ¦¬ν•΄λ‘μ—ˆμœΌλ‹ˆ, Basic Conceptκ³Ό 4계측에 λŒ€ν•œ λ‚΄μš©μ„ λ¨Όμ € κ°„λ‹¨νžˆλΌλ„ 읽고 였면 이해가 쑰금 더 λΉ λ₯Ό 것이닀.
예λ₯Ό λ“€μ–΄, DataλΌλŠ” ꡬ쑰체에 100μ΄λΌλŠ” 숫자λ₯Ό κ°–κ³  있고, ν•΄λ‹Ή κ°’μ˜ μ£Όμ†Œκ°€ 0x10이라고 ν•΄λ³΄μž. 만일 Serialization 없이 Data ꡬ쑰체λ₯Ό κ·ΈλŒ€λ‘œ 기둝해버리면, 이λ₯Ό λ°›λŠ” μž…μž₯μ—μ„œλŠ” 0x10만 λ°›κΈ° λ•Œλ¬Έμ— μ˜¬λ°”λ₯Έ 값을 받지 λͺ»ν•˜κ²Œ λœλ‹€. λ”°λΌμ„œ Data ꡬ쑰체가 가진 값을 μ˜¬λ°”λ₯΄κ²Œ ν•΄μ„μ‹œν‚€κΈ° μœ„ν•΄μ„ , 0x10을 κΈ°λ‘ν•˜λŠ” 것이 μ•„λ‹ˆλΌ 이λ₯Ό μ—­μ°Έμ‘°ν•˜μ—¬ 100μ΄λΌλŠ” 값을 써야 λœλ‹€. λ¬Όλ‘  μ‹€μ œ Serialization은 이와 같이 λ‹¨μˆœν•œ 포인터 β†’ κ°’ λ³€ν™˜μ΄ μ•„λ‹ˆλΌ, 객체 κ°„ 상속 μ—¬λΆ€, 객체 λ‚΄ ν¬μΈν„°μ˜ μˆœν™˜ μ—¬λΆ€ λ“± λ‹€λ₯Έ 상황듀도 κ³ λ €λ˜μ–΄μ•Ό ν•œλ‹€. 이처럼 Serialization을 μ‹€μ œλ‘œ λ§Œλ“€μ–΄ λ‚΄κΈ° μœ„ν•΄μ„  정말 λ§Žμ€ κ°œλ…λ“€μ΄ ν¬ν•¨λ˜λŠ”λ°, λͺ¨λ“  λ‚΄μš©λ“€μ„ 이번 μ„œλΈŒμ νŠΈμ— κ΅¬ν˜„ν•˜λŠ” 것은 λΆˆκ°€λŠ₯에 κ°€κΉŒμšΈ 뿐만 μ•„λ‹ˆλΌ 범주도 λ²—μ–΄λ‚œλ‹€. λ”°λΌμ„œ ex01μ—μ„œ μš”κ΅¬ν•˜λŠ” Serialization은 κ·Έ 의미만 μ΄μš©λ˜μ—ˆλ‹€λŠ” 점을 μ΄ν•΄ν•˜κ³ , λ‹¨μˆœνžˆ ν˜„μž¬ Process에 λŒ€ν•΄μ„œλ§Œ 적용될 수 μžˆλŠ” κ°„λ‹¨ν•œ μ½”λ“œλ₯Ό 톡해 reinterpret_cast<T>λ₯Ό μ΄ν•΄ν•˜λŠ” 것을 λͺ©ν‘œλ‘œ ν•˜λ©΄ λœλ‹€.
μœ„μ—μ„œ μ–ΈκΈ‰ν•œ 뢀뢄듀이 적용되면, Serialization은 크게 5κ°€μ§€λ‘œ λ‚˜λ‰œλ‹€. 이에 λŒ€ν•΄μ„  μ•„λž˜ 글을 μ°Έκ³ ν•˜μž.

3. ex02 (Identify real type)

dynamic_cast<T>?

상속 ꡬ쑰λ₯Ό λ§Œλ“€μ–΄ μ—… μΊμŠ€νŒ… ν›„, μžμ‹ μ˜ νƒ€μž…μ΄ 무엇인지 λ°νžˆλŠ” 것이 ex02의 λͺ©ν‘œμ΄λ‹€. μš°μ„  λ²”μš©μ μΈ νƒ€μž…μ„ ν™•μΈν•˜λŠ” 과정은 μ—¬κΈ°μ„œ μ‚¬μš©ν•˜λŠ” 방식과 사뭇 λ‹€λ₯΄μ§€λ§Œ, 상속 κ΅¬μ‘°μ—μ„œ λ‹€ν˜•μ„±μ„ κ°–λŠ” κ°μ²΄λ“€μ˜ Identification은 dynamic_cast<T>λ₯Ό μ΄μš©ν•  수 μžˆλ‹€. 일전에 Module 03μ—μ„œ μ§μ ‘μ μœΌλ‘œ λ‹€μš΄ μΊμŠ€νŒ…μ„ μ–ΈκΈ‰ν•˜μ—¬ dynamic_cast<T>λ₯Ό λ³΄μ˜€κ³ , κ·Έ 이후에도 μ’…μ’… dynamic_castλ₯Ό μ΄μš©ν–ˆλ‹€. μ—¬κΈ°μ„œλŠ” μ–΄λ–»κ²Œ 포인터에 λŒ€ν•΄μ„œ μžμ‹ μ˜ νƒ€μž…μ„ 인식할 수 μžˆμ„μ§€, μ°Έμ‘°μžμ— λŒ€ν•΄μ„œ μžμ‹ μ˜ νƒ€μž…μ„ 인식할 수 μžˆλŠ”μ§€μ— λŒ€ν•΄μ„œ 이해할 수 있게, dynamic_cast<T>에 λŒ€ν•΄ μ„Έμ„Ένžˆ μ•Œμ•„λ³Ό 것이닀.
기본적으둜 staticμ΄λΌλŠ” μ˜λ―ΈλŠ” 컴파일 νƒ€μž„μ— νŒŒμ•…μ΄ κ°€λŠ₯ν•˜λ‹€λΌλŠ” 의미고, dynamicμ΄λΌλŠ” μ˜λ―ΈλŠ” 런 νƒ€μž„μ΄ λ˜μ–΄μ„œμ•Ό νŒŒμ•…μ΄ κ°€λŠ₯ν•˜λ‹€ μ •λ„λ‘œ 이해할 수 μžˆμ„ 것이닀. dynamic_cast<T>의 dynamic도 이 의미λ₯Ό 크게 λ²—μ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.

RTTI (Runtime Type Information) on dynamic_cast<T>

#include <iostream> class A { std::string s; public: A(void) : s("Base") {} void text(void) { std::cout << s << std::endl; } }; class B : public A { std::string s; public: B(void) : A(), s("Derived") {} void text(void) { std::cout << s << std::endl; } }; int main(void) { B b; A* a_ptr = &b; B* b_ptr = a_ptr; b_ptr->text(); return (0); }
C++
상속 ꡬ쑰λ₯Ό κ°–λŠ” 객체듀 κ°„μ—λŠ” 기반 클래슀λ₯Ό λ©”λͺ¨λ¦¬ 상에 κ°–κ³  있기 λ•Œλ¬Έμ— νŒŒμƒ 클래슀λ₯Ό 기반 클래슀의 ν¬μΈν„°λ‘œ μ°Έμ‘°ν•˜λŠ” 것이 κ°€λŠ₯ν–ˆκ³ , λ”°λΌμ„œ μ—… μΊμŠ€νŒ… μ‹œμ—λŠ” μ•„λ¬΄λŸ° λ¬Έμ œκ°€ μ—†μ—ˆλ‹€. ν•˜μ§€λ§Œ νŒŒμƒ ν΄λž˜μŠ€κ°€ μ—… μΊμŠ€νŒ… 된 기반 클래슀 ν˜•νƒœκ°€ μ•„λ‹ˆλΌ μˆœμˆ˜ν•œ 기반 클래슀λ₯Ό μ΄μš©ν•˜λŠ” 경우라면, νŒŒμƒ 클래슀λ₯Ό λ©”λͺ¨λ¦¬ 상에 μœ μ§€ν•˜κ³  μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— νŒŒμƒ 클래슀둜의 λ‹€μš΄ μΊμŠ€νŒ…μ€ λ¬Έμ œκ°€ 될 수 μžˆλ‹€. 이 λ•Œλ¬Έμ— 기본적으둜 μ»΄νŒŒμΌλŸ¬λŠ” λ‹€μš΄ μΊμŠ€νŒ…μ„ κΈˆμ§€ν•˜κ³ , 이와 같은 μ‹œλ„λ₯Ό ν•˜λ©΄ μ—λŸ¬λ₯Ό λ‚΄μ€€λ‹€.
#include <iostream> class A { std::string s; public: A(void) : s("Base") {} void text(void) { std::cout << s << std::endl; } }; class B : public A { std::string s; public: B(void) : A(), s("Derived") {} void text(void) { std::cout << s << std::endl; } }; int main(void) { B b; A* a_ptr = &b; B* b_ptr = static_cast<B*>(a_ptr); b_ptr->text(); return (0); }
C++
그런데 νŒŒμƒ 클래슀 β†’ 기반 클래슀 β†’ νŒŒμƒ 클래슀둜의 μΊμŠ€νŒ…μ€ λ¬Έμ œκ°€ 없지 μ•Šμ€κ°€? λ”°λΌμ„œ ν•΄λ‹Ή κ²½μš°μ—λŠ” 기반 클래슀λ₯Ό νŒŒμƒ 클래슀둜 λ³€ν™˜ν•˜λŠ” 것이 κ°€λŠ₯ν•΄μ•Ό ν•˜λŠ”λ°, μ»΄νŒŒμΌλŸ¬μ—κ²Œ 기반 클래슀λ₯Ό νŒŒμƒ 클래슀둜 μΈμ‹μ‹œν‚€λ„λ‘ static_cast<T>λ₯Ό μ΄μš©ν•  μˆ˜λ„ μžˆμ§€ μ•Šμ€κ°€? λ§žλŠ” 말이닀. static_cast<T>λ₯Ό μ΄μš©ν•˜μ—¬ 기반 클래슀λ₯Ό νŒŒμƒ 클래슀둜 μΈμ‹μ‹œν‚€λ©΄, μ»΄νŒŒμΌλ„ 되고 싀행도 잘 λ˜λŠ” 것을 확인할 수 μžˆλ‹€.
#include <iostream> class A { std::string s; public: A(void) : s("Base") {} void text(void) { std::cout << s << std::endl; } }; class B : public A { std::string s; public: B(void) : A(), s("Derived") {} void text(void) { std::cout << s << std::endl; } }; int main(void) { A a; A* a_ptr = &a; B* b_ptr = static_cast<B*>(a_ptr); b_ptr->text(); return (0); }
C++
ν•˜μ§€λ§Œ 이와 같은 static_cast<T>의 μ΄μš©μ€ νŒŒμƒ 클래슀 β†’ 기반 클래슀 β†’ νŒŒμƒ 클래슀 뿐만 μ•„λ‹ˆλΌ 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€μ— λŒ€ν•΄μ„œλ„ μ—λŸ¬λ₯Ό λ§Œλ“€μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, μœ„μ™€ 같이 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€λŠ” 싀행이 λ˜μ–΄μ„œμ•Ό 런 νƒ€μž„ μ—λŸ¬κ°€ λ‚˜λŠ” 것을 확인할 수 μžˆλ‹€. 즉, 사전에 컴파일 νƒ€μž„μ—μ„œ μ—λŸ¬λ₯Ό μ°Ύμ•„λ‚Ό 수 μ—†κΈ° λ•Œλ¬Έμ— μ‹€μ œ μƒν™©μ—μ„œλŠ” κ½€λ‚˜ 머리가 μ•„ν”ˆ 상황을 λ§žμ„ μˆ˜λ„ μžˆλ‹€.
#include <iostream> class A { std::string s; public: A(void) : s("Base") {} void text(void) { std::cout << s << std::endl; } }; class B : public A { std::string s; public: B(void) : A(), s("Derived") {} void text(void) { std::cout << s << std::endl; } }; int main(void) { A a; A* a_ptr = &a; B* b_ptr = dynamic_cast<B*>(a_ptr); b_ptr->text(); return (0); }
C++
λ”°λΌμ„œ 이처럼 νŒŒμƒ 클래슀 β†’ 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€μ— λŒ€ν•΄μ„  ν—ˆμš©ν•˜λ©΄μ„œλ„, 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€μ— λŒ€ν•΄μ„œλŠ” 막을 수 μžˆλŠ” 방법이 ν•„μš”ν•œλ°, 이 λ•Œ μ΄μš©λ˜λŠ” 것이 dynamic_cast<T>이닀. dynamic_cast<T>λ₯Ό μ΄μš©ν•˜λ©΄ μœ„μ²˜λŸΌ 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€μ— λŒ€ν•΄μ„œ 컴파일 νƒ€μž„μ— μ—λŸ¬λ₯Ό λ‚΄λŠ” 것을 확인할 수 μžˆλ‹€. κ·Έλ ‡λ‹€λ©΄ 만일 A 클래슀의 text ν•¨μˆ˜μ— virtual ν‚€μ›Œλ“œκ°€ 뢙은 κ²½μš°μ—λŠ” μ–΄λ–»κ²Œ λ™μž‘ν•˜κ²Œ 될 까? μ΄λŠ” static_cast<T>와 dynamic_cast<T>의 νŠΉμ„±μœΌλ‘œ 이해할 수 μžˆλ‹€.
static_cast<T>λŠ” B 클래슀의 ν¬μΈν„°λ‘œ 할당을 μœ„ν•΄ A 클래슀의 포인터λ₯Ό μΌμ‹œμ μœΌλ‘œ ν˜• λ³€ν™˜ν•œ κ²ƒμ΄λ―€λ‘œ, B 클래슀의 포인터가 μ°Έμ‘°ν•˜λŠ” 곡간은 μ—¬μ „νžˆ A 클래슀 νƒ€μž…μœΌλ‘œ μ΄ν•΄λœλ‹€. λ”°λΌμ„œ textλ₯Ό ν˜ΈμΆœν–ˆμ„ λ•ŒλŠ” vTableμ—μ„œ μ‹€μ œ νƒ€μž…μΈ A 클래슀의 textλ₯Ό ν˜ΈμΆœν•˜κ²Œ λœλ‹€.
λ°˜λ©΄μ— dynamic_cast<T>λŠ” B 클래슀의 ν¬μΈν„°λ‘œ 할당을 μœ„ν•΄ A 클래슀의 포인터λ₯Ό ν˜• λ³€ν™˜ν•˜λŠ” 과정이 static_cast<T>μ™€λŠ” 사뭇 λ‹€λ₯΄λ‹€. A 클래슀의 ν¬μΈν„°λ‘œ μ°Έμ‘°λ˜λŠ” 객체가 B 클래슀의 ν¬μΈν„°λ‘œ 참쑰될 수 μžˆλŠ”μ§€ λ¨Όμ € ν™•μΈν•˜κ³ , 기반 클래슀 β†’ νŒŒμƒ ν΄λž˜μŠ€μ— ν•΄λ‹Ήν•˜λ―€λ‘œ λ³€ν™˜μ΄ λΆˆκ°€λŠ₯ν•˜λ‹€λŠ” 것을 μΈμ§€ν•˜μ—¬ b_ptrμ—λŠ” NULL이 ν• λ‹Ήλœλ‹€. λ”°λΌμ„œ NULL의 textλ₯Ό ν˜ΈμΆœν•˜λ € ν–ˆμœΌλ―€λ‘œ SegFaultκ°€ λ°œμƒν•œλ‹€. 이처럼 dynamic_cast<T>λŠ” ν˜• λ³€ν™˜ κ°€λŠ₯ μ—¬λΆ€λ₯Ό 확인할 수 μžˆλ„λ‘ λ˜μ–΄ 있기 λ•Œλ¬Έμ—, μ—¬λŸ¬ 상황에 λŒ€ν•΄μ„œ λŒ€μ²˜ν•  수 μžˆλ‹€.
dynamic_cast<T>둜 ν˜• λ³€ν™˜ν•  수 없을 λ•Œ Tκ°€ 무엇인지에 따라 λ°˜ν™˜ 값이 λ‹€λ₯Έλ°, μ΄λŠ” μ•„λž˜μ—μ„œ 닀룬닀. 만일 ν˜• λ³€ν™˜μ΄ κ°€λŠ₯ν•˜λ‹€λ©΄ static_cast<T>μ—μ„œλŠ” μΌμ‹œμ μœΌλ‘œ ν˜• λ³€ν™˜μ΄ λ˜μ—ˆλ˜ 것과 달리, dynamic_cast<T>의 κ²°κ³ΌλŠ” μΈμ‹λ˜λŠ” νƒ€μž…μ„ μ•„μ˜ˆ λ°”κΎΈκ²Œ λ§Œλ“ λ‹€.
dynamic_cast<T>이 μœ„μ™€ 같이 λ™μž‘ν•  수 μžˆλŠ” 것은 RTTI (Runtime Type Information)μ΄λΌλŠ” C++ 컴파일러의 κΈ°λŠ₯ 덕뢄이닀. μ΄λ¦„μ—μ„œ μ•Œ 수 μžˆλ“―μ΄, 객체의 μœ ν˜•μ„ 런 νƒ€μž„μ— κ²°μ •ν•  수 μžˆλ„λ‘ λ§Œλ“œλŠ” 것이닀. 이와 같은 κΈ°λŠ₯은 객체가 μœ„μΉ˜ν•œ λ©”λͺ¨λ¦¬ 상에 객체 νƒ€μž…μ— λŒ€ν•œ 정보λ₯Ό μΆ”κ°€ν•˜λ©΄μ„œ, μ‹€μ œ νƒ€μž…μ„ μ΄μš©ν•˜μ§€ μ•Šκ³  제곡된 νƒ€μž…μ„ μ΄μš©ν•˜λ©΄μ„œ μ΄λ€„μ§ˆ 수 μžˆλ‹€. λ”°λΌμ„œ dynamic_cast<T>λ₯Ό μ΄μš©ν•˜λ©΄ μ‹€μ œλ‘œλŠ” A 클래슀 νƒ€μž…μ΄μ§€λ§Œ, ν˜• λ³€ν™˜μ— λ”°λ₯Έ κ²°κ³ΌλŠ” 이와 λ‹€λ₯Ό 수 있게 λ˜λŠ” 것이닀. 결둠적으둜 μ—… μΊμŠ€νŒ…μ„ μ œμ™Έν•œ 상속 ꡬ쑰의 객체 κ°„μ˜ ν˜• λ³€ν™˜ (λ‹€μš΄ μΊμŠ€νŒ…, 크둜슀 μΊμŠ€νŒ…)μ—μ„œλŠ” RTTIλ₯Ό 기반으둜 λ™μž‘ν•˜λŠ” dynamic_cast<T>λ₯Ό μ“°λŠ” 것이 μ•ˆμ „ν•˜λ‹€.
dynamic_cast<T>의 μ΄μš©μ€ μ—… μΊμŠ€νŒ…μ—μ„œλ„ μ΄μš©ν•  수 μžˆμ§€λ§Œ, 주둜 λ‹€μš΄ μΊμŠ€νŒ… ν˜Ήμ€ 크둜슀 μΊμŠ€νŠΈμ—μ„œ μ΄μš©λœλ‹€. μ–ΈκΈ‰λœ 두 ν˜• λ³€ν™˜μ€ μ•„λž˜ λ§ν¬μ—μ„œ μžμ„Έν•˜κ²Œ λ‹€λ€„μ Έμžˆλ‹€.

dynamic_cast<void *>

#include <iostream> class A { public: virtual ~A(void) {} }; class B : public A { }; class C : public A { }; class D : public B, public C { }; int main(void) { D d; B* b_ptr = &d; C* c_ptr = &d; if (dynamic_cast<A*>(b_ptr) == dynamic_cast<A*>(c_ptr)) std::cout << "Same" << std::endl; else std::cout<< "Not Same" << std::endl; return (0); }
C++
dynamic_cast<T>μ—μ„œ Tλ₯Ό void*λ₯Ό 주게 되면 쑰금 νŠΉλ³„ν•œ 역할을 ν•˜κ²Œ λœλ‹€. 상속 κ΅¬μ‘°μ—μ„œ μ΅œν•˜μœ„μ— μ‘΄μž¬ν•˜λŠ” νŒŒμƒ 클래슀λ₯Ό μ°Έμ‘°ν•˜λŠ” void*둜 ν˜• λ³€ν™˜ν•˜κ²Œ λœλ‹€. 이와 같은 κΈ°λŠ₯을 ν™•μΈν•˜κΈ° μœ„ν•΄ 닀이아λͺ¬λ“œ 상속 ν˜•νƒœλ‘œ ν΄λž˜μŠ€λ“€μ„ μ •μ˜ν•˜μ˜€κ³ , μ–΅μ§€μŠ€λŸ½κΈ΄ ν•˜μ§€λ§Œ 곡톡 상속 ν΄λž˜μŠ€λŠ” virtual둜 두지 μ•Šμ•˜λ‹€. D 클래슀둜 μ •μ˜λœ d κ°μ²΄λŠ” B ν΄λž˜μŠ€κ°€ λ˜μ–΄λ„, C ν΄λž˜μŠ€κ°€ λ˜μ–΄λ„ 두 비ꡐ 값은 κ°™μ•„μ•Ό ν•˜λŠ”λ°, μ‹€ν–‰ κ²°κ³ΌλŠ” 그렇지 μ•Šμ€ 것을 λ³Ό 수 μžˆλ‹€.
두 μ£Όμ†Œμ˜ 비ꡐλ₯Ό μœ„ν•΄ B ν΄λž˜μŠ€μ™€ C 클래슀λ₯Ό λͺ¨λ‘ A 클래슀둜 μ—… μΊμŠ€νŒ… ν–ˆλ‹€.
#include <iostream> class A { public: virtual ~A(void) {} }; class B : public A { }; class C : public A { }; class D : public B, public C { }; int main(void) { D d; B* b_ptr = &d; C* c_ptr = &d; if (dynamic_cast<void*>(b_ptr) == dynamic_cast<void*>(c_ptr)) std::cout << "Same" << std::endl; else std::cout<< "Not Same" << std::endl; return (0); }
C++
λ”°λΌμ„œ 두 μ£Όμ†Œκ°€ λ™μΌν•˜λ‹€λŠ” 것을 dynamic_cast<void*>λ₯Ό μ΄μš©ν•˜μ—¬ μ΅œν•˜μœ„μ— μ‘΄μž¬ν•˜λŠ” νŒŒμƒ 클래슀의 μ‹œμž‘ μœ„μΉ˜λ₯Ό μ–»μ–΄λ‚΄μ–΄ λΉ„κ΅ν•˜λ©΄, μœ„ 결과처럼 λ™μΌν•˜λ‹€λŠ” κ²°κ³Όλ₯Ό μ–»μ–΄λ‚Ό 수 μžˆλ‹€.
// dynamic_cast<T>(void*) -> Error
C++
μ£Όμ˜ν•  μ μœΌλ‘œλŠ” dynamic_cast<void*>λŠ” κ°€λŠ₯ν•˜μ§€λ§Œ dynamic_cast<T>λ₯Ό μˆ˜ν–‰ν•˜λ €λŠ” 인자λ₯Ό void*둜 μ‚¬μš©ν•˜λ©΄ μ•ˆ λœλ‹€.

Failure on Pointer & Reference on dynamic_cast<T>

// dynamic_cast<T*> -> When Error, NULL returned // dynamic_cast<T&> -> When Error, Exception throwed
C++
ex02의 dynamic_cast<T>의 결둠에 λ‹¬ν–ˆλ‹€. dynamic_cast<T>의 Tλ‘œλŠ” 포인터가 올 μˆ˜λ„ 있고, μ°Έμ‘°μžκ°€ 올 μˆ˜λ„ μžˆλ‹€. dynamic_cast<T>의 ν˜• λ³€ν™˜ μ‹€νŒ¨λŠ” 기본적으둜 NULL을 기반으둜 ν•œλ‹€κ³  μƒκ°ν•˜λ©΄ νŽΈν•˜λ‹€. ν¬μΈν„°μ˜ 경우 T*에 λŒ€ν•œ NULL은 μ‘΄μž¬ν•  수 μžˆμœΌλ―€λ‘œ ν˜• λ³€ν™˜ μ‹€νŒ¨ μ‹œ NULL이 λ°˜ν™˜λ˜κ³ , 참쑰자의 경우 NULL에 λŒ€ν•œ μ°Έμ‘°λŠ” λΆˆκ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— Exception을 λ˜μ§€κ²Œ λœλ‹€.
λ”°λΌμ„œ dynamic_cast<T>λ₯Ό μ΄μš©ν•˜μ—¬ 상속 κ΅¬μ‘°μ—μ„œ 자기 μžμ‹ μ˜ νƒ€μž…μ΄ 무엇인지 μ•Œ 수 μžˆλŠ” 방법은 λ‹€μŒκ³Ό κ°™λ‹€. Tκ°€ ν¬μΈν„°λ‘œ λ“€μ–΄μ˜¨ κ²½μš°μ—λŠ” NULL을 λ°˜ν™˜ν•˜μ§€ μ•Šμ€ κ²½μš°κ°€ 자기 μžμ‹ μ˜ νƒ€μž…μ΄κ³ , Tκ°€ 참쑰자둜 λ“€μ–΄μ˜¨ κ²½μš°μ—λŠ” Exception이 λ˜μ Έμ§€μ§€ μ•Šμ€ κ²½μš°κ°€ 자기 μžμ‹ μ˜ νƒ€μž…μ΄λ‹€.

4. Code of Jseo