[42Seoul] CPP06 type_casting
Additional rule
The following rule applies to the entire module and is not optional.
For each exercise, the type conversion must be solved using one specific type of casting.
Your choice will be checked during defense.
Exercise 00
문자열 표현을 매개 변수로 사용하는 프로그램을 작성한다. 이 리터럴은 다음 스칼라 유형 중 하나에 속해야 합니다.
- char literals : 'c', 'a'
간단하게 하기 위해, 표시할 수 없는 문자는 입력으로 사용해서는 안 된다는 점에 유의하십시오. 문자로 변환할 수 없는 경우 에서는 유용한 메시지를 인쇄합니다. - int literals: 0, -42, 42
- float literals: 0.0f, -4.2f, 4.2f
-inff, +inff and nanf 에 대해서 다뤄야 합니다. - double literals: 0.0, -4.2, 4.2
-inf, +inf and nan 에 대해서 다뤄야 합니다.
먼저 매개 변수로 전달된 리터럴 유형을 감지하고 문자열에서 실제 유형으로 변환한 다음 다른 세 가지 데이터 유형으로 명시적으로 변환해야 합니다. 마지막으로 아래와 같이 결과를 표시합니다. (순서에 대해서 제대로 하지 않으면, fail을 주는 카뎃도 존재...)
변환이 의미가 없거나 오버플로우되는 경우 유형 변환이 불가능함을 사용자에게 알리는 메시지를 표시합니다.
숫자 한계 및 특수 값을 처리하는 데 필요한 헤더를 포함합니다.
isprint (참고)
: 인쇄 가능한 문자 인지 확인
return : A value different from zero (i.e., true) if indeed c is a printable character. Zero (i.e., false) otherwise.
#include <cctype>
int isprint ( int c );
isascii
: ascii code에 포함되는 범위 인지 확인
numeric_limits<T>::min(number), ::max(number) (참고)
: 라이브러리가 컴파일되는 특정 플랫폼에서 산술 형식 (정수 또는 부동 소수점) 의 속성에 대한 정보를 제공
#include <limits>
std::numeric_limits<T>::멤버 함수
strtol (참고)
: 값 으로 반환되는 지정된 base 의 정수로 해석 하는 C 문자열 str 을 구문 분석
예제에서 10진수라고 한다면, 숫자가 아닌 문자열에 pEnd는 가르키게 됩니다.
#include <cstdlib>
long int strtol (const char* str, char** endptr, int base);
// example
#include <cstdlib>
#include <iostream>
int main ()
{
char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff";
char * pEnd;
long int li1, li2, li3, li4;
li1 = strtol (szNumbers,&pEnd,10);
li2 = strtol (pEnd,&pEnd,16);
li3 = strtol (pEnd,&pEnd,2);
li4 = strtol (pEnd,NULL,0);
std::cout << li1 << std::endl;
std::cout << li2 << std::endl;
std::cout << li3 << std::endl;
std::cout << li4 << std::endl;
return 0;
}
strtod (참고)
: 부동 소수점 숫자(현재 로케일에 따라)로 해석 하는 C 문자열 strdouble 을 구문 분석하고 해당 값을 . endptr 이 null 포인터 가 아니면 함수는 endptr 값도 숫자 뒤의 첫 번째 문자를 가리키도록 설정, 사용은 위와 동일합니다.
#include <cstlib>
double strtod (const char* str, char** endptr);
strtof, strtoll 등의 함수들은 c++11에 사용이 가능하며, isNan 같은 경우에도 c++11 입니다
quiet_NaN (참고)
: Returns the special value "quiet not-a-number", as represented by the floating-point type
static_cast(참고1, 참고2)
: 사용할 수 있는 가장 간단한 유형의 캐스트입니다. 컴파일 타임 캐스트 입니다 . 유형 간의 암시적 변환(예: int에서 float 또는 void*에 대한 포인터)과 같은 작업을 수행하며 명시적 변환 함수(또는 암시적 변환 함수)를 호출할 수도 있습니다.
static_cast로 값들이 어떻게 변하는 건지 여러 가지 테스트를 해보면 서브젝트에서 어떻게 요구를 하는 건지, 문자열의 변환을 어떻게 하는 건지 파악을 하게 되면 좀 더 수월하게 구현할 수 있을 거 같습니다. (다 완성하고 수정하기에는 오래 걸릴 거 같아서... 저는 PASS)
Exercise01
uintptr_t serialize(Data* ptr);
포인터를 받고 부호 없는 정수 uintptr_t로 변환합니다.
Data* deserialize(uintptr_t raw);
부호 없는 정수를 입력 인자로 받고 Data에 대한 포인터로 변환합니다.
반드시 비어있지 않은(데이터 멤버가 있음을 의미)Data 구조체를 만들어야 합니다.
Data 객체의 주소에 serialize()를 사용하세요 그리고 deserialize()로 반환하세요.
deserialize()의 반환 값이 이전 포인터와 동일한지 확인합니다.
Data 구조체 파일을 제출하는 것을 잊지마세요.
uintptr_t (참고)
: unsigned integer type capable of holding a pointer to void (포인터를 보이드로 고정할 수 있는 부호 없는 정수형)
: 임의의 포인터 타입끼리 변환을 허용하는 캐스트 연산자 입니다. 또한 정수형을 포인터로 바꿀 수도 있습니다. (이때 정수값이 포인터의 절대 주소로 들어가게 됩니다. -> 위험) expression에 해당하는 것을 new_type으로 비트단위로 바꾸는 것 입니다.
reinterpret_cast < newType > ( expression )
참고 사이트를 통해서 예제 소스가 나와있고, 그것을 통해서 사용에 대한 것을 알 수 있다. (그게 ex01의 전부였다...)
CPP05와 같이 마지막 문제때문에 리트라이를 할 수 있다는 판단에 진행
Exercise 02
공용 가상 소멸자만 있는 기본 클래스를 구현합니다. Base에서 공개적으로 상속하는 빈 클래스 A, B 및 C를 세 개 만듭니다.
(언급된 네 가지 클래스는 Orthodox Canonical Form 으로 설계될 필요가 없습니다)
Base * generate(void)
: A, B 또는 C를 무작위로 인스턴스화하고 인스턴스를 기본 포인터로 반환합니다. 랜덤 선택 구현을 위해 원하는 모든 것을 자유롭게 사용하십시오.
void identify(Base* p)
: 이것은 "A", "B" 또는 "C"로 표시된 물체의 실제 유형을 인쇄합니다.
void identify(Base& p)
: 이것은 "A", "B" 또는 "C"로 표시된 물체의 실제 유형을 인쇄합니다. 이 기능 내에서 포인터를 사용할 수 없습니다.
typeinfo header는 포함할 수 없습니다.
type casting (참고)
: 주어진 유형의 표현식을 다른 유형으로 변환하는 것
Implicit conversion(암시적 변환)
// 암시적 변환
short a = 42;
int b;
b = a;
/* ------------------ */
// B 클래스 의 개체를 매개 변수로 사용하는 생성자가 있기 때문에 클래스 A 와 클래스 B 의 개체 간에 암시적 변환이 발생
class A {};
class B { public: B (A a) {} };
A a;
B b = a;
Explicit conversion(명시적 변환)
// 명시적 변환
short a = 42;
int b;
b = (int)a; // c-like casting
b = int (a); // functional
/* ------------------ */
// 전통적인 명시적 유형 캐스팅을 사용하면 포인터가 가리키는 유형과 관계없이 모든 포인터를 다른 포인터 유형으로 변환할 수 있습니다. 멤버 result 에 대한 후속 호출 은 런타임 오류 또는 예기치 않은 결과를 생성
#include <iostream>
using namespace std;
class CDummy {
float i,j;
};
class CAddition {
int x,y;
public:
CAddition (int a, int b) { x=a; y=b; }
int result() { return x+y;}
};
int main () {
CDummy d;
CAddition * padd;
padd = (CAddition*) &d; // 명시적 유형 캐스팅을 사용하여 호환되지 않는 다른 유형의 개체에 대한 참조를 할당
cout << padd->result();
return 0;
}
Four specific casting operators
: dynamic_cast, static_cast, reinterpret_cast, const_cast
: casting operator <new_type> (expression)
dynamic_cast (참고)
: 객체에 대한 포인터 및 참조에만 사용할 수 있습니다. 그 목적은 형식 변환의 결과가 요청된 클래스의 유효한 완전한 개체인지 확인하는 것입니다. dynamic_cast 의 유형 안전성 검사 오버헤드가 방지됩니다.
first dynamic_cast는 필수 클래스의 완전한 개체가 아니기 때문에 포인터를 캐스팅할 수 없습니다. 실패를 나타내기 위해 null 포인터를 반환합니다.
dynamic_cast 는 관련 없는 클래스에 대한 포인터 사이에서도 널 포인터를 캐스팅할 수 있으며 모든 유형의 포인터를 무효 포인터 (void*)로 캐스팅할 수도 있습니다. 캐스트가 실패하고 new-type 이 참조 유형이면 std::bad_cast 유형의 핸들러와 일치하는 예외가 발생합니다
static_cast
: 파생 클래스에서 기본 클래스로뿐만 아니라 기본 클래스에서 파생 클래스로의 관련 클래스에 대한 포인터 간에 변환을 수행할 수 있습니다. 이렇게 하면 적절한 개체가 변환되는 경우 최소한 클래스가 호환되지만 변환 중인 개체가 실제로 대상 유형의 전체 개체인지 확인하기 위해 런타임 중에 안전 검사가 수행되지 않습니다. 따라서 변환이 안전한지 확인하는 것은 프로그래머에게 달려 있습니다.
downcast (static_cast<자식클래스>(부모클래스))시에는 unsafe하게 동작할 수 있습니다.
이 외에도 일반 변수 타입간의 static_cast를 사용합니다. double -> int, float -> double와 같은 형변환을 의미 합니다.
그리고 포인터 타입 배열의 static_cast도 가능합니다.
reinterpret_cast
: 관련되지 않은 클래스를 포함하여 모든 포인터 유형을 다른 포인터 유형으로 변환합니다
연산 결과는 한 포인터에서 다른 포인터로 값의 간단한 이진 복사본입니다. 모든 포인터 변환이 허용됩니다. 가리키는 내용이나 포인터 유형 자체가 검사되지 않습니다.
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a);
이것은 유효한 C++ 코드이지만 그다지 의미가 없습니다.
const_cast
: 설정하거나 제거할 개체의 상수를 조작합니다.
#include <iostream>
int main(void)
{
char str[] = "Mamma";
const char * ptr = str;
char * c = const_cast<char *>(ptr);
c[0] = 'S';
std::cout << str << std::endl;
return 0;
}