etc./StackOverFlow

일반 캐스트 대 static_cast 대 dynamic_cast [중복]

청렴결백한 만능 재주꾼 2021. 12. 24. 06:18
반응형

질문자 :Graeme Perrow


저는 거의 20년 동안 C 및 C++ 코드를 작성해 왔지만 이러한 언어의 한 측면에서 제가 정말 이해하지 못한 부분이 있습니다. 나는 분명히 일반 캐스트를 사용했습니다.

 MyClass *m = (MyClass *)ptr;

도처에 있지만 두 가지 다른 유형의 캐스트가 있는 것 같으며 차이점을 모르겠습니다. 다음 코드 줄의 차이점은 무엇입니까?

 MyClass *m = (MyClass *)ptr; MyClass *m = static_cast<MyClass *>(ptr); MyClass *m = dynamic_cast<MyClass *>(ptr);


static_cast

'static_cast'는 기본적으로 몇 가지 제한 사항과 추가 사항이 있는 암시적 변환을 되돌리려는 경우에 사용됩니다. 'static_cast'는 런타임 검사를 수행하지 않습니다. 이것은 특정 유형의 객체를 참조한다는 것을 알고 있으므로 확인이 필요하지 않은 경우에 사용해야 합니다. 예시:
 void func(void *data) { // Conversion from MyClass* -> void* is implicit MyClass *c = static_cast<MyClass*>(data); ... } int main() { MyClass c; start_thread(&func, &c) // func(&c) will be called .join(); }

MyClass 객체를 전달했다는 것을 알고 있으므로 이를 확인하기 위해 런타임 검사가 필요하지 않습니다.

dynamic_cast

'dynamic_cast'는 객체의 동적 유형이 무엇인지 모를 때 유용합니다. 참조된 객체에 기본 클래스로 캐스팅된 유형이 포함되어 있지 않으면 null 포인터를 반환합니다(참조로 캐스팅할 때 이 경우 'bad_cast' 예외가 발생함).
 if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) { ... } else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) { ... }

다운캐스트(파생 클래스로 캐스팅)하고 인수 유형이 다형성이 아닌 경우 dynamic_cast 사용할 수 없습니다. 예를 들어 다음 코드는 Base 가상 기능이 포함되어 있지 않기 때문에 유효하지 않습니다.

 struct Base { }; struct Derived : Base { }; int main() { Derived d; Base *b = &d; dynamic_cast<Derived*>(b); // Invalid }

"up-cast"(기본 클래스로 캐스트)는 항상 static_castdynamic_cast 모두에서 유효하며 캐스트 없이도 유효합니다. "up-cast"는 암시적 변환(기본 클래스에 액세스할 수 있다고 가정합니다. public 상속).

레귤러 캐스트

이러한 캐스트를 C 스타일 캐스트라고도 합니다. dynamic_cast 고려하지 않고 작동하는 첫 번째 C++ 캐스트를 사용하는 것과 동일합니다. const_cast , static_castreinterpret_cast 모두 결합하므로 훨씬 더 강력 dynamic_cast 사용하지 않기 때문에 안전하지도 않습니다.

또한 C 스타일 캐스트를 사용하면 이를 수행할 수 있을 뿐만 아니라 비공개 기본 클래스로 안전하게 캐스트할 수 있으며 "동등한" static_cast 시퀀스는 이에 대한 컴파일 시간 오류를 제공합니다.

어떤 사람들은 간결함 때문에 C 스타일 캐스트를 선호합니다. 숫자형 캐스트에만 사용하고 사용자 정의 유형이 포함될 때는 더 엄격한 검사를 제공하므로 적절한 C++ 캐스트를 사용합니다.


Johannes Schaub - litb

정적 캐스트

정적 캐스트는 호환되는 유형 간의 변환을 수행합니다. C 스타일 캐스트와 유사하지만 더 제한적입니다. 예를 들어, C 스타일 캐스트는 정수 포인터가 char를 가리키는 것을 허용합니다.
 char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes

이로 인해 할당된 메모리의 1바이트를 가리키는 4바이트 포인터가 생성되기 때문에 이 포인터에 쓰는 것은 런타임 오류를 일으키거나 일부 인접 메모리를 덮어씁니다.

 *p = 5; // run-time error: stack corruption

C 스타일 캐스트와 달리 정적 캐스트를 사용하면 컴파일러가 포인터와 pointee 데이터 형식이 호환되는지 확인할 수 있으므로 프로그래머가 컴파일 중에 이 잘못된 포인터 할당을 잡을 수 있습니다.

 int *q = static_cast<int*>(&c); // compile-time error

캐스트 재해석

강제로 포인터 변환을 하려면 C 스타일 캐스트가 백그라운드에서 하는 것과 같은 방식으로 재해석 캐스트가 대신 사용됩니다.

 int *r = reinterpret_cast<int*>(&c); // forced conversion

이 캐스트는 한 포인터 유형에서 다른 호환되지 않는 포인터 유형으로의 변환과 같이 관련되지 않은 특정 유형 간의 변환을 처리합니다. 기본 비트 패턴을 변경하지 않고 단순히 데이터의 이진 복사를 수행합니다. 이러한 저수준 작업의 결과는 시스템에 따라 다르므로 이식할 수 없습니다. 완전히 피할 수 없다면 주의해서 사용해야 합니다.

다이나믹 캐스트

이것은 객체 포인터와 객체 참조를 상속 계층 구조의 다른 포인터나 참조 유형으로 변환하는 데만 사용됩니다. 포인터가 대상 유형의 완전한 개체를 참조하는지 런타임 검사를 수행하여 가리키는 개체가 변환될 수 있는지 확인하는 유일한 캐스트입니다. 이 런타임 검사가 가능하려면 개체가 다형성이어야 합니다. 즉, 클래스는 하나 이상의 가상 함수를 정의하거나 상속해야 합니다. 컴파일러가 이러한 개체에 필요한 런타임 형식 정보만 생성하기 때문입니다.

동적 캐스트 예

아래 예에서 MyChild 포인터는 동적 캐스트를 사용하여 MyBase 포인터로 변환됩니다. 하위 개체에 완전한 기본 개체가 포함되어 있기 때문에 이 파생에서 기본으로의 변환이 성공합니다.

 class MyBase { public: virtual void test() {} }; class MyChild : public MyBase {}; int main() { MyChild *child = new MyChild(); MyBase *base = dynamic_cast<MyBase*>(child); // ok }

MyBase 포인터를 MyChild 포인터로 변환하려고 시도합니다. 기본 개체에 완전한 자식 개체가 포함되어 있지 않기 때문에 이 포인터 변환은 실패합니다. 이를 나타내기 위해 동적 캐스트는 null 포인터를 반환합니다. 이것은 런타임 동안 변환이 성공했는지 여부를 확인하는 편리한 방법을 제공합니다.

 MyBase *base = new MyBase(); MyChild *child = dynamic_cast<MyChild*>(base); if (child == 0) std::cout << "Null pointer returned";

bad_cast 예외가 발생하여 동적 캐스트가 실패합니다. try-catch 문을 사용하여 처리해야 합니다.

 #include <exception> // … try { MyChild &child = dynamic_cast<MyChild&>(*base); } catch(std::bad_cast &e) { std::cout << e.what(); // bad dynamic_cast }

동적 또는 정적 캐스트

동적 캐스트를 사용하는 이점은 프로그래머가 런타임 중에 변환이 성공했는지 여부를 확인할 수 있다는 것입니다. 단점은 이 검사를 수행하는 것과 관련된 성능 오버헤드가 있다는 것입니다. 이러한 이유로 첫 번째 예에서는 정적 캐스트를 사용하는 것이 더 좋았습니다. 파생에서 기본으로의 변환은 절대 실패하지 않기 때문입니다.

 MyBase *base = static_cast<MyBase*>(child); // ok

그러나 두 번째 예에서는 변환이 성공하거나 실패할 수 있습니다. MyBase MyBase 인스턴스가 포함되어 있으면 실패 MyChild 인스턴스가 포함되어 있으면 성공합니다. 어떤 상황에서는 런타임까지 이것이 알려지지 않을 수 있습니다. 이 경우 동적 캐스트가 정적 캐스트보다 더 나은 선택입니다.

 // Succeeds for a MyChild object MyChild *child = dynamic_cast<MyChild*>(base);

기본에서 파생된 변환이 동적 캐스트 대신 정적 캐스트를 사용하여 수행되었다면 변환이 실패하지 않았을 것입니다. 불완전한 개체를 참조하는 포인터를 반환했을 것입니다. 이러한 포인터를 역참조하면 런타임 오류가 발생할 수 있습니다.

 // Allowed, but invalid MyChild *child = static_cast<MyChild*>(base); // Incomplete MyChild object dereferenced (*child);

상수 캐스트

const 수정자를 추가하거나 제거하는 데 사용됩니다.

 const int myConst = 5; int *nonConst = const_cast<int*>(&myConst); // removes const

const 캐스트를 사용하면 상수 값을 변경할 수 있지만 그렇게 하는 것은 여전히 런타임 오류를 일으킬 수 있는 잘못된 코드입니다. 이는 예를 들어 상수가 읽기 전용 메모리 섹션에 있는 경우 발생할 수 있습니다.

 *nonConst = 10; // potential run-time error

const 캐스트는 pointee를 수정하지 않더라도 상수가 아닌 포인터 인수를 취하는 함수가 있을 때 주로 사용됩니다.

 void print(int *p) { std::cout << *p; }

const 캐스트를 사용하여 함수에 상수 변수를 전달할 수 있습니다.

 print(&myConst); // error: cannot convert // const int* to int* print(nonConst); // allowed

출처 및 추가 설명


Community Wiki

C++ 프로그래밍/타입 캐스팅 기사를 살펴봐야 합니다.

여기에는 다양한 캐스트 유형에 대한 좋은 설명이 포함되어 있습니다. 위 링크에서 가져온 내용은 다음과 같습니다.

const_cast

const_cast(expression) const_cast<>()는 변수의 const(ness)(또는 volatile-ness)를 추가/제거하는 데 사용됩니다.

static_cast

static_cast(expression) static_cast<>()는 정수 유형 간에 캐스트하는 데 사용됩니다. 'eg' char->long, int->short 등

정적 캐스트는 관련 유형에 대한 포인터를 캐스팅하는 데에도 사용됩니다(예: void*를 적절한 유형으로 캐스팅).

dynamic_cast

동적 캐스트는 런타임에 포인터와 참조를 변환하는 데 사용되며 일반적으로 포인터나 참조를 상속 체인(상속 계층 구조) 위 또는 아래로 캐스팅할 목적으로 사용됩니다.

dynamic_cast(표현식)

대상 유형은 포인터 또는 참조 유형이어야 하며 표현식은 포인터 또는 참조로 평가되어야 합니다. 동적 캐스트는 식이 참조하는 개체의 형식이 대상 형식과 호환되고 기본 클래스에 하나 이상의 가상 멤버 함수가 있는 경우에만 작동합니다. 그렇지 않고 캐스트되는 표현식의 유형이 포인터이면 NULL이 반환되고 참조에 대한 동적 캐스트가 실패하면 bad_cast 예외가 throw됩니다. 실패하지 않으면 동적 캐스트는 표현식이 참조한 개체에 대한 대상 유형의 포인터 또는 참조를 반환합니다.

재해석_캐스트

재해석 캐스트는 단순히 한 유형을 다른 유형으로 비트 단위로 캐스트합니다. 모든 포인터 또는 정수 유형은 재해석 캐스트를 사용하여 다른 유형으로 캐스트될 수 있으므로 쉽게 오용될 수 있습니다. 예를 들어, reinterpret cast one을 사용하면 안전하지 않게 정수 포인터를 문자열 포인터로 캐스팅할 수 있습니다.


TJ Seabrooks

참고로 저는 Bjarne Stroustrup이 C 스타일 캐스트를 피해야 하며 가능하면 static_cast 또는 dynamic_cast를 사용해야 한다고 말한 것으로 믿습니다.

Barne Stroustrup의 C++ 스타일 FAQ

당신이 원하는 것을 위해 그 조언을 받아들이십시오. 나는 C++ 전문가와는 거리가 멀다.


Jason Baker

C 스타일 캐스트를 사용하지 마십시오.

C 스타일 캐스트는 const 및 재해석 캐스트가 혼합되어 있으며 코드에서 찾기 및 바꾸기가 어렵습니다. C++ 애플리케이션 프로그래머는 C 스타일 캐스트를 피해야 합니다.


ugasoft

C 스타일 캐스트는 const_cast, static_cast 및 reinterpret_cast를 병합합니다.

C++에 C 스타일 캐스트가 없었으면 합니다. C++ 캐스트는 적절하게 눈에 띄고(물론 캐스트는 일반적으로 나쁜 일을 하고 있음을 나타냄) 캐스트가 수행하는 다양한 종류의 변환을 적절하게 구분합니다. 그들은 또한 일관성 관점에서 볼 때 매우 좋은 boost::lexical_cast와 같이 비슷하게 보이는 함수를 작성할 수 있습니다.


DrPizza

dynamic_cast 는 포인터 및 참조 유형만 지원합니다. NULL 반환하고 형식이 참조 형식인 경우 예외를 throw합니다. 따라서 dynamic_cast 는 객체가 주어진 유형인지 확인하는 데 사용할 수 있지만 static_cast 는 할 수 없습니다(단순히 잘못된 값으로 끝날 것입니다).

C 스타일(및 기타) 캐스트는 다른 답변에서 다뤘습니다.


larsmoa

dynamic_cast 에는 런타임 유형 검사가 있으며 참조 및 포인터에서만 작동하는 반면 static_cast 는 런타임 유형 검사를 제공하지 않습니다. 전체 정보는 MSDN 문서 static_cast Operator 를 참조하십시오.


Inisheer

출처 : http:www.stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast

반응형