왜 캐스팅을 피해야합니까? [닫은]
나는 일반적으로 형변환 유형을 가능한 한 많이 피합니다. 코딩 관행이 좋지 않고 성능이 저하 될 수 있습니다.
하지만 누군가 나에게 그 이유를 정확히 설명 해달라고하면 나는 아마도 헤드 라이트에있는 사슴처럼 볼 것입니다.
왜 / 언제 캐스팅이 나쁜가요?
Java, C #, C ++에 대해 일반적입니까? 아니면 모든 운영 환경에서 자체 용어로 처리하고 있습니까?
모든 언어에 대한 세부 사항은 환영합니다. 예를 들어 C ++에서 왜 나쁜가요?
여기에 세 가지 언어로 태그를 달았는데 세 언어의 대답은 정말하는데. C ++에 대한 논의는 C 캐스트에 대한 논의도 의미하며, 네 번째 답을 제공합니다.
명시 적으로 언급하지 않은 것 C로 시작하겠습니다. C 캐스트에는 많은 문제가 있습니다. 하나는 다양한 일을 할 수있는 것입니다. 어떤 경우에는 캐스트가 컴파일러에게 (본질적으로) "종료, 내가하는 일을 알고 있습니다"라고 말하는 것 이상을 수행하지 않습니다. 즉, 문제를 해결하는 수있는 변환을 수행 할 수 있습니다. 예를 들어 char a=(char)123456;
. 정의 된이 구현의 정확한 결과 (크기 서명 및 정도에 따라 다름) char
), 다소 이상한 상황을 제외하고 아마도 유용하지 않을 것입니다. C 캐스트는 또한 발생 타임에만 발생하는 것인지 (즉, 컴파일러에게 일부 데이터를 해석 / 처리하는 방법을 알려주는 것임) 실행에 발생하는 것 (예 : 이중에서 실제 변환)에 따라 발생합니다. 긴).
C ++는 여러 "새로운"캐스트 연산자를 추가하여 어느 정도이를 처리하려고 시도합니다. 각 연산자는 C 캐스트 기능의 하위 집합으로 만 제한됩니다. 당신이 경우 -이 (예를 들어) 실수로 당신이 정말로 의도하지 않은 CHAPTER 2 할 더 어렵게 단지 당신이 사용할 수있는 object-에 CONST와 멀리 캐스팅 의도을 const_cast
하고 있는지 것일 만 이 영향을 줄 수있는 일이 있는지 여부입니다 객체는 const
, volatile
또는 아닙니다. 반대로,이 static_cast
object-인지 여부에 영향을 할 수 없습니다 const
또는 volatile
. 요컨대, 대부분의 동일한 유형의 기능을 사용하고 한 캐스트가 일반적으로 한 가지 유형의 변환 만 수행 할 수 있도록 분류되어 단일 C 캐스트가 한 번의 작업으로 2 ~ 3 개의 변환을 수행 할 수 있습니다. 예외는 주된 적어도 일부 경우 에 대신 를 사용할 수 있으며 으로 작성 되었음에도 불구하고 실제로 . 예를 들어, 클래스 계층을 위 또는 아래로 이동하는 데 사용할 수"아래로"는 "위"로 캐스트하는 것이 항상 안전하지 않습니다. 동적으로 수행됩니다.dynamic_cast
static_cast
dynamic_cast
static_cast
dynamic_cast
Java와 C #은 서로 훨씬 더 유사합니다. 특히 둘 다 캐스팅은 (가상?) 항상 작동 작업입니다. C ++ 연산자의 경우 캐스트 일반적으로 dynamic_cast
실제로 수행되는 작업의 관점 에서 에 추론 가깝습니다. 대상, 개체를 특정 유형으로 캐스트하려고 할 때 컴파일러는 해당 변환이 허용 여부를 확인하기 위해 실행 검사를 삽입합니다. , 않은 경우 예외를 던집니다. 세부 사항 (예 : "불량 캐스트"예외에 사용되는)은 다양하지만 기본 이름은 거의 유사합니다 (메모리가 제공되는 경우 Java는 int
C에 훨씬 더 가까운 몇 가지 비 클래스 유형에 캐스트를 적용합니다. 캐스트 -그러나 그러나 1) 확실히 기억하지 못하며 2) 사실이 매우 중요하지 않습니다.
좀 더 일반적으로 살펴보면 상황은 매우 간단합니다 (적어도 IMO). 캐스트 (분명히 충분히)는 특정 유형에서 다른 유형으로 문제를 변환하고 있음을 의미합니다. 그렇게하면 "왜?"라는 질문이 제기됩니다. 어떤 것이 냐고 구체적으로 유형이 정의되지 않은 이유는 무엇입니까? 그것은 어떤 것이 아니라 말하는 것이 아니라 , 그것이 일어날 때마다 올바른 유형이 전체에 사용되는 코드를 재 설계 할 수 있는지에 대한 질문입니다. 겉보기에 무해한 변환 (예 : 정수와 부동 소수점 사이)조차도 일반적인 것보다 훨씬 더 면밀히 조사해야합니다. 그들의 모습 에도 불구하고유사성, 정수는 "계산 된"유형의 경우 사용되어야하며 "측정 된"유형의 경우 부동 소수점을 사용합니다. 구별을 무시하는 것은 "평균적인 미국 가정은 1.8 명의 자녀가있다"와 같은 말도 안되는 말로 이어진다. 우리 모두 그 일이 어떻게 일어나는지 알 수있는 사실은 1.8 명의 자녀가있는 가족이있는 것 입니다. 1 개 또는 2 개 또는 그 이상일 수 1.8 개는 없습니다.
여기에 좋은 답변이 많이 있습니다. 다음은 내가 보는 방식입니다 (C # 관점에서).
캐스팅은 일반적으로 다음 두 가지 중 하나를 의미합니다.
이 일상의 작동 유형을 알고 싶습니다 컴파일러는이를. 컴파일러는 사용할 수있는 곳에서 사용할 수 있습니다. 현재 유행이 표현이 유형을 사용하고 있습니다. 내가 주어진 코드를 생성하거나, 내가 틀린 예외를 던진다.
컴파일러와 개발자 모두 식의 실행 유형을 알고 있습니다. 이 경우에는 어떤 유형의 번호가 있습니다. 주어진 유형의 값에서 원하는 유형의 값을 생성하는 코드를 생성합니다. 그렇게 할 수있는 예외를 던지십시오.
은 그것들 반대 라는 점에 유의하십시오 . 두 종류의 캐스트가 있습니다! 현실 에 대한 힌트 를 컴파일러에 제공하는 캐스트가 있습니다.이 유형의 객체는 고객 유형입니다. 컴파일러에게 한 유형에서 다른 유형으로 매핑 을 수행하도록 지시 하는 캐스트가 있습니다 . 이 double에 해당하는 int가 필요합니다.
두 종류의 캐스트는 모두 적신호입니다. 첫 번째 유형의 캐스트는 "왜 개발자가 컴파일러가 모르는 것을 아는 것이 정확히 무엇입니까?"라는 질문을 제기합니다. 그런 상황에 처해 가능한 일반적으로 컴파일러 가 실제로 처리 할 수있는 프로그램을 변경하는 것이 좋습니다. 그러면 캐스트가 필요하지 않습니다. 분석은 타임에 수행됩니다.
두 번째 유형의 캐스트는 "처음에 대상 데이터 유형에서 작업이 수행되지 않는 이유는 무엇입니까?"라는 질문을 제기합니다. int에서 결과가 필요하다면 왜 처음에 더블을 제출합니까? int를 포함하지 않는 것이 좋습니까?
여기에 몇 가지 추가 생각 :
http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/
오류는 항상 Java에서 실행 오류로보고됩니다. 제네릭 또는 템플릿을 사용하면 오류가 발생하는 경우 오류로 실수를 저지른 경우 쉽게 감지 할 수 있습니다.
위에서 말했듯이. 모든 캐스팅이 나쁘다는 것은 아닙니다. 그렇게하는 것이 최선의 방법입니다.
수단은 본질적으로 나쁘지 즉, 실제로는 전혀 수행 할 것입니다. 또는 더 우아하게 캐스팅되어야한다고 달성하기위한 수단으로 종종 오용되는 것입니다.
그것이 보편적으로 나쁘다면 언어는 그것을 지원하지 않을 것입니다. 다른 언어 기능과 그 자리가 있습니다.
제 조언은 귀하의 기본 언어에 점점 늘어나고 모든 캐스트 및 관련 모범 사례를 이해하는 것입니다. 그것은 다른 언어로의 여행을 알려줄 것입니다.
관련 C # 문서는 여기에 있습니다 .
이전 SO 질문에서 C ++ 옵션에 대한 훌륭한 요약이 여기에 있습니다 .
여기서는 주로 C ++에 대해 말하고 대부분은 Java 및 C #에도 적용됩니다.
C ++는 정적으로 형식화 된 언어 입니다. 언어가 허용하는 약간의 여유가 있습니다 (가상 함수, 암시 적 변환) 기본적으로 컴파일러는 모든 개체의 유형을 알고 있습니다. 언어를 사용 이러한하는 이유 는 컴파일 타임에 오류 가 발생할 수 있기 때문 입니다. 의 유형을 컴파일러 알고있는 경우 a
와 b
,이 할 때 당신 그것은 컴파일 time-에 당신을 잡을을 구석으로입니다 a=b
경우 a
복잡한 숫자와 b
문자열입니다.
당신이 명시 적 캐스팅을 할 때마다 당신이 생각하기 때문에 당신이 닥쳐 컴파일러에게 당신이 더 잘 알고 . 틀린 경우 일반적으로 런타임 에만 알 수 있습니다 . 그리고 런타임에서 알아내는 문제는 이것이 고객의 것일 수 있다는 것입니다.
Java, C # 및 C ++는 강력한 유형의 언어이지만 강력한 유형의 언어는 융통성이 없다고 볼 수 있지만 컴파일 타임에 유형 검사를 수행하는 이점이 있으며 특정 작업에 대해 잘못된 유형을 사용하여 발생하는 런타임 오류로부터 사용자를 보호합니다.
기본적으로 두 가지 종류의 캐스트가 있습니다. 하나는 더 일반적인 유형에 대한 캐스트 또는 다른 유형에 대한 캐스트 (더 구체적)입니다. 보다 일반적인 유형으로 캐스팅하면 (부모 유형으로 캐스팅) 컴파일 시간 검사가 그대로 유지됩니다. 그러나 다른 유형 (보다 구체적인 유형)으로 캐스팅하면 컴파일 시간 유형 검사가 비활성화되고 런타임 검사에 의해 컴파일러로 대체됩니다. 이것은 컴파일 된 코드가 올바르게 실행될 것이라는 확신이 적다는 것을 의미합니다. 또한 추가 런타임 유형 검사로 인해 성능에 약간의 영향을 미칩니다 (Java API는 캐스트로 가득 차 있습니다!).
일부 유형의 주조는 매우 안전하고 효율적이므로 주조로 간주되지 않는 경우가 많습니다.
파생 형식에서 기본 형식으로 캐스트하는 경우 일반적으로 매우 저렴하며 (종종 언어, 구현 및 기타 요인에 따라 비용이 들지 않음) 안전합니다.
int와 같은 간단한 유형에서 long int와 같은 더 넓은 유형으로 캐스트하면 다시 종종 상당히 저렴하며 (일반적으로 캐스트와 동일한 유형을 할당하는 것보다 훨씬 비싸지 않습니다) 다시 안전합니다.
다른 유형은 더 많거나 더 비쌉니다. 대부분의 언어에서 기본 유형에서 파생 유형으로 캐스트하는 것은 저렴하지만 심각한 오류 위험이 높습니다 (C ++에서는 기본에서 파생으로 static_cast하는 경우 저렴하지만 기본 값이 파생 유형이 아닌 경우 동작이 정의되지 않았고 매우 이상 할 수 있음) 또는 상대적으로 비용이 많이 들고 예외가 발생할 위험이 있습니다 (C ++의 dynamic_cast, C #의 명시 적 base-to-derived 캐스트 등). Java 및 C #의 Boxing은 이것의 또 다른 예이며 훨씬 더 많은 비용 (기본 값이 처리되는 방식보다 더 많이 변경된다는 점을 고려할 때)입니다.
다른 유형의 캐스트는 정보를 잃을 수 있습니다 (긴 정수 유형에서 짧은 정수 유형으로).
이러한 위험 사례 (예외 또는 더 심각한 오류) 및 비용은 모두 캐스팅을 피해야하는 이유입니다.
더 개념적이지만 더 중요한 이유는 캐스팅의 각 사례가 코드의 정확성에 대해 추론 할 수있는 능력이 훼손된 경우이기 때문입니다. 각 사례는 무언가 잘못 될 수있는 또 다른 장소이며 그 방식이 시스템 전체가 잘못 될지 여부를 추론하는 복잡성을 추가 할 수 있습니다. 캐스트가 매번 안전하다고 입증하더라도 이것이 추론의 추가 부분임을 증명합니다.
마지막으로 캐스트를 많이 사용하면 개체 모델을 생성하거나 사용하거나 둘 다 고려하지 못했음을 나타낼 수 있습니다. 동일한 몇 가지 유형간에 자주 앞뒤로 캐스트하는 것은 거의 항상 유형 간의 관계를 고려하지 않는 것입니다. 익숙한. 캐스트가 나쁜 일의 신호이기 때문에 캐스트가 나쁘지는 않습니다.
프로그래머가 언어 기능 사용에 대한 독단적 규칙 ( "XXX를 사용하지 마십시오!", "XXX는 유해한 것으로 간주 됨"등)에 집착하는 경향이 증가하고 있습니다. 여기서 XXX는 goto
s에서 protected
데이터 멤버에 대한 포인터, 싱글 톤, 객체 전달에 이르기까지 다양 합니다. 값.
내 경험상 그러한 규칙을 따르는 것은 두 가지를 보장합니다. 당신은 끔찍한 프로그래머가 될 수 없으며 훌륭한 프로그래머가되지 않을 것입니다.
훨씬 더 나은 접근 방식은 이러한 포괄적 인 금지 뒤에 숨겨진 진실의 핵심을 파헤쳐 서 밝혀낸 다음 기능을 현명하게 사용하는 것입니다.이 기능 은 작업에 가장 적합한 도구 가 많은 상황 이 있다는 것을 이해하고 있습니다 .
"일반적으로 가능한 한 캐스팅 유형을 최대한 피합니다."는 이러한 지나치게 일반화 된 규칙의 좋은 예입니다. 캐스트는 많은 일반적인 상황에서 필수적입니다. 몇 가지 예 :
- 타사 코드와 상호 운용 할 때 (특히 해당 코드가
typedef
s 로 가득 찬 경우 ). (예 :GLfloat
<->double
<->Real
.) - 파생에서 기본 클래스 포인터 / 참조로 캐스팅 : 이것은 매우 일반적이고 자연스러워 컴파일러가 암시 적으로 수행합니다. 명시 적으로 만들면 가독성이 높아진다면 캐스트는 뒤로가 아니라 앞으로 나아가는 것입니다!
- 기본에서 파생 클래스 포인터 / 참조로 캐스팅 : 잘 설계된 코드에서도 일반적입니다. (예 : 이기종 컨테이너)
- 이진 직렬화 / 역 직렬화 또는 기본 제공 형식의 원시 바이트에 액세스해야하는 기타 하위 수준 코드 내부.
- 다른 유형을 사용하는 것이 더 자연스럽고 편리하며 읽기 쉬운 경우 언제든지. (예 :
std::size_type
->int
.)
확실히 캐스트를 사용하는 것이 적절 하지 않은 상황이 많이 있으며, 이것도 배우는 것이 중요합니다. 위의 답변이 그중 일부를 지적하는 데 도움이되었으므로 너무 자세히 설명하지는 않겠습니다.
KDeveloper의 답변 에 대해 자세히 설명하기 위해 본질적으로 형식이 안전하지 않습니다. 캐스팅을 사용하면 캐스팅 및 캐스팅 대상이 일치한다는 보장이 없으며, 이러한 경우 런타임 예외가 발생하며 이는 항상 나쁜 일입니다.
C #과 관련하여 C #에는 is
및 as
연산자가 포함되어 있기 때문에 캐스트가 성공할지 여부를 결정할 수있는 기회가 있습니다. 따라서 작업이 성공할지 여부를 결정하고 적절하게 진행하려면 적절한 단계를 수행해야합니다.
C #의 경우 값 유형을 처리하는 동안 관련된 boxing / unboxing 오버 헤드로 인해 캐스팅하는 동안 더주의해야합니다.
누군가 이미 이것을 언급했는지 확실하지 않지만 C # 캐스팅에서는 다소 안전한 방식으로 사용할 수 있으며 종종 필요합니다. 여러 유형이 될 수있는 오브젝트를 수신한다고 가정하십시오. is
키워드를 사용하여 먼저 객체가 실제로 캐스팅하려는 유형인지 확인한 다음 객체를 해당 유형으로 직접 캐스팅 할 수 있습니다. (저는 Java를 많이 사용하지 않았지만 거기에서도 매우 간단한 방법이 있다고 확신합니다.)
두 가지 조건이 충족되는 경우에만 개체를 특정 유형으로 캐스팅합니다.
- 당신은 그것이 그 유형이라는 것을 알고 있습니다
- 컴파일러는하지 않습니다
이것은 당신이 가지고있는 모든 정보가 당신이 사용하는 타입 구조에서 잘 표현되지 않음을 의미합니다. 구현이 의미 론적 으로 모델을 구성 해야 하므로이 경우에는 분명히 그렇지 않습니다.
이제 캐스트를 할 때 두 가지 이유가있을 수 있습니다.
- 유형 관계를 표현하는 데 나쁜 일을했습니다.
- 언어 유형 시스템은 단순히 표현할만큼 표현력이 부족합니다.
대부분의 언어에서 두 번째 상황에 자주 직면합니다. Java에서와 같은 Generics는 C ++ 템플릿 시스템에 더 많은 도움이되지만 마스터하기가 어렵고 일부 작업은 불가능하거나 노력할 가치가 없을 수 있습니다.
따라서 캐스트는 특정 언어로 특정 유형 관계를 표현하기 위해 문제를 우회하는 더러운 해킹이라고 말할 수 있습니다. 더러운 해킹은 피해야합니다. 그러나 그들 없이는 결코 살 수 없습니다.
일반적으로 템플릿 (또는 제네릭)은 캐스트보다 형식이 안전합니다. 그런 점에서 캐스팅 문제는 형식 안전성이라고 말하고 싶습니다. 그러나 특히 다운 캐스팅과 관련된 또 다른 미묘한 문제는 디자인입니다. 적어도 내 관점에서 다운 캐스팅은 코드 냄새, 내 디자인에 문제가있을 수 있다는 표시이며 더 조사해야합니다. 이유는 간단합니다. 추상화를 올바르게 "얻는"경우에는 필요하지 않습니다! 그런데 좋은 질문 ...
건배!
간결하게 말하면 좋은 이유는 이식성 때문입니다. 둘 다 동일한 언어를 수용하는 다른 아키텍처는 예를 들어 다른 크기의 int를 가질 수 있습니다. 따라서 내가 ArchA에서 더 좁은 int를 가진 ArchB로 마이그레이션하면 기껏해야 이상한 동작을 볼 수 있고 최악의 경우 seg 오류를 볼 수 있습니다.
(저는 아키텍처 독립적 인 바이트 코드와 IL을 분명히 무시하고 있습니다.)
참고 URL : https://stackoverflow.com/questions/4167304/why-should-casting-be-avoided
'IT' 카테고리의 다른 글
Flask SQLAlchemy 쿼리, 열 이름 지정 (0) | 2020.08.27 |
---|---|
Visual Studio : 여러 빌드 후 명령? (0) | 2020.08.27 |
Matlab : 명령 줄에서 m- 파일 실행 (0) | 2020.08.27 |
jQuery DataTables : 제어 테이블 너비 (0) | 2020.08.27 |
Apache :“AuthType이 설정되지 않은 것!” (0) | 2020.08.27 |