C++ - Module 06

이호용·2021년 9월 26일
1

cpp

목록 보기
7/16

C++ - Module 06

C++ Casts
캐스팅에 대해 공부해보자.

C++ provides a variety of ways to cast between types:
다음과 같은 5가지 방법의 캐스팅을 제공한다.

  1. static_cast
  2. reinterpret_cast
  3. const_cast
  4. dynamic_cast
  5. C-style casts

서로다른 cast들은 다음과 같은 일반적인 형식이 있습니다.

cast_name<cast_to_type>(item_to_cast)

서로다른 케스트를 살펴보자.

1. static_cast

static_castC++ 캐스팅 세계에서 가장 중요한 역할을 합니다. static_cast유형 간의 암시적 변환을 처리합니다(예: 정수 유형 변환, 에 대한 모든 포인터 유형 void*). static_cast명시적 변환 함수를 호출할 수도 있습니다.

c에서 사용하던, 형변환과 비슷한데, static_cast로 하면 장점은 아래처럼 변환이 안되야하는건 컴파일 에러가 나오면서 표시해준다.

int main() {
    double* dou = new double(10.1234);
    int* integer_1 = dou;    //묵시적 형변환
    int* integer_2 = (int*)dou;   //명시적 형변환
    int* integer_3 = static_cast<int*>(dou); //static_cast 형변환
}

위의 예제에서 integeer_1과 integer_3은 에러가 나오면서 컴파일 안됨. 하지만, 명시적 형변환은 그냥 컴파일됨. 그래서 에러찾기 힘듬

에러나는 이유는 dou는 double의 데이터가 담긴 주소로 되어있음.

그래서 dou를 int*형 주소로 변환하면 안에 값은 그대로 인데, 해당 주소 사용방법만 바뀜. 고로 짤리거나 쨋든 이상하게 나옴.

바꿀거면

int *integer_1 = &(static_cast<int>(*dou))

이렇게 넣어야지? 실행은 안해봤어요. 맞겠죠? 혹시 틀렸으면 댓글 주세요.

int main()
{
	int x = 10, y = 4;
    
    std::cout << x/y << std::endl;
    std::cout << (double)x / y << std::endl; //아래 static cast 와 같다. 
    std::cout << static_cast<double>(x) / y << std::endl;
}
int main() {
    double dou = 10.1234;
    int integer_1 = dou;    //묵시적 형변환
    int integer_2 = (int)dou;   //명시적 형변환
    int integer_3 = static_cast<int>(dou); //static_cast 형변환
    }

요약!!! : static_cast를 쓰는 이유는 명시적이면서! 안전하게 사용할 수 있다!

2. reinterpret_cast

  1. reinterpret_cast는 임의의 포인터 타입끼리 변환을 허용하는 캐스트 연산자 입니다.
	int a;
    int *ptr2 = &a;
    char * c;
    c = reinterpret_cast<char *>(ptr2);
  1. 또한 정수 계열 형식이 포인터 형식으로 변환될 수 있도록 하고 그 반대로도 변환될 수 있도록 합니다.
 12 uintptr_t serialize(Data *ptr)
 13 {
 14     //reinterpret_cast 은 임의의 포인터 타입끼리 변환을 허용
 15     // 또는 포인터를 정수로 변환: 그 반대도 가능.
 16     return reinterpret_cast<uintptr_t>(ptr);
 17 }
 
 25 int main( void )
 26 {
 27     Data *A = new Data;
 28
 29     A->s1 = "hi";
 30     A->n = 42;
 31     A->s2 = "bye";
 
 34     uintptr_t tmp =  serialize( A );
  
 35 }

3. const_cast

const 를 지우는데 사용한다.

int main(void){
    char str[] = "BlockDMask";
    const char * ptr = str;
    cout << "before : " << str << endl;
    
    char * c = const_cast<char *>(ptr);
    }

이러면 c[0] = 'b' 이렇게 바꿀수 있음. 짱짱

4. dynamic_cast

dynamic_cast는 safe downcasting(안전한 다운캐스팅)에 사용이 됩니다

부모클래스의 포인터에서 자식 클래스의 포인터로 다운캐스팅해주는 연산자 입니다.
여기서 주의 할점, 자식에서 부모로 만들었다 다시 부모에서 자식으로 갈때는 안전하지만 그 외의 상황에 다운캐스팅을 하면 애초에 자식에 대한 데이터가 없으므로 에러 날 수 있음

부모에서 자식으로 캐스팅 되는지 확인하고 바꾸기 때문에 런타임이 많이 발생함.

성공할 경우 : new_type의 value를 return 합니다.
실패할 경우(new_type = pointer) : null pointer
실패할 경우(new_type = reference) : bad_cast (exception 처리됩니다.)

"자식클래스의 생성자로 생성되었고 자식클래스 포인터가 가리키고 있는 클래스"를 부모클래스로 형변환 할때는 dynamic_cast를 사용하지 않고 static_cast를 사용합니다.

5. C-style casts

이건 젤 쉽다..

지금까지 사용햇던 일반적인 캐스팅을 C-style caste라고 한다.

double a;
int b = (int)a;

이런식으로 강제로 캐스팅 하는 방법과

3 + 3.5

이렇게 다른 형끼리 더하거나 사용할일이 잇으면
바이트가 더 큰 형태로 자동형변환 된다.

상속에서 형변환

부모 객체 Base
자식 객체 Derived

Base *b = new Derived;
이렇게 부모 클래스에 자식클래스를 넣는건 가능하다.
이런걸 업케스팅.(자식 클래스 포인터 자동으로 부모클래스 포인터로 변환됨)

uintptr_t , intptr_t

uintptr_t은 intptr_t의 부호가 없는 버젼에 해당한다.

포인터의 크기는 애플리케이션의 호환성과 다른 환경으로의 이식 가능성을 고민할때 문제가 된다. 최근에 널리 사용되는 대부분의 운영체제 환경에서 포인터의 크기는 일반적으로 포인터 타입에 상관없다. 예를 들면 char에대한 포인터는 구조체에 대한 포이터와 크기가 같다. C표준에서 모든 데이터 타입에 대한 포인터의 크기가 같아야 한다고 명시하고 있지는 않지만, 일반적으로 포인터의 크기는 동일하다. 하지만 함수에 대한 포인터와 데이터에 대한 포인터의 크기는 다를 수도 있다.

포인터의 크기는 사용하는 장비와 컴파일러에 따라 다르다. 예를 들어 윈도우버전에서 포인터의 크기는 32 또는 64bit이며 오래된 dos 그리고 왼도우 3.1 os에서 포인터는 16또는 32비트다.

  1. 사전 정의된 포인터 관련 데이터 타입
  • 포인터를 다룰 때 다음 네가지의 사전 정의된 데이터 타입이 종종 사용된다
    1) size_t -> 안전한 크기 타입 제공을 위해 사용 : 메모리 크기를 나타내기 때문에 음수를 제외한 양수만 나타낸다. 주소 지정이 가능한 메로리 영역과 일치하는 크기를 선언하는 이식 가능한 방법을 제공하기 위해서이다.

2) ptrdiff_t -> 포인터 연산을 처리하기 위해 사용

3) intptr->t와 uintptr_t -> 포인터 주소를 저장하기 위해 사용
: intptr_t와 uintptr_t 타입은 포인터의 주소를 저장하는데 사용된다.
이 두 타입은 다른 환경으로 이식이 가능하고 안전한 포인터 선언 방법을 제공하며, 시스템 내부에서 사용하는 포인터와 같은 크기다. 포인터를 정수 표현으로 변환할때 유용하게 사용할수 있다.

int num;

intptr_t *pi = &num; // 에러
uintptr_t *pu = &num; // 에러

타입이 맞지 않아 생기는 에러가 발생한다.

uintptr_t *pu = (uinttpr_t *)&num;

이렇게 캐스팅 후 대입해준다.

아키텍처의 포인터 유형을 보유 할 수있는 정수 유형을 원하는 일반적인 이유는 포인터에 대해 정수 특정 연산을 수행하거나 포인터를 정수 "핸들"로 제공하여 포인터 유형을 모호하게하기 위해서입니다.

라고 하는데 쉽게말하면, 아키텍쳐마다 (int ) 나 (double )크기 같이 주소의 크기가 다를수 있는데 이러한 이식을 좀더 안전하게 하기위해서 uintptr_t로 정수로 변환하여 저장해두는데 사용한다.

0개의 댓글