답변:
스위치 케이스에는 거의 항상 케이스가 있어야합니다 default
.
사용하는 이유 default
1. 예기치 않은 값을 '잡기'
switch(type)
{
case 1:
//something
case 2:
//something else
default:
// unknown type! based on the language,
// there should probably be some error-handling
// here, maybe an exception
}
2. 특별한 행동을위한 경우 인 '기본'행동을 다루기 위해.
메뉴 중심 프로그램과 bash 쉘 스크립트에서 이것을 많이 볼 수 있습니다. 또한 스위치 케이스 외부에서 변수가 선언되었지만 초기화되지 않은 경우에이를 확인할 수 있으며 각 경우마다 다른 것으로 초기화됩니다. 여기서 기본값은 변수에 액세스하는 라인 코드가 오류를 일으키지 않도록 초기화해야합니다.
3. 해당 사례를 다룬 코드를 읽는 사람에게 보여주기 위해.
variable = (variable == "value") ? 1 : 2;
switch(variable)
{
case 1:
// something
case 2:
// something else
default:
// will NOT execute because of the line preceding the switch.
}
이것은 지나치게 단순화 된 예이지만 요점은 코드를 읽는 사람이 왜 variable
1 또는 2 이외의 것이 될 수 없는지 궁금 하지 않아야한다는 것입니다.
내가 사용하지 않음으로 생각할 수있는 유일한 경우 default
입니다 스위치가 아니라 분명 다른 모든 대안을 행복하게 무시 될 수있는 무언가를 확인하는 경우
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
// no default really required here
}
enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;
유효한 MyEnum
인스턴스를 만듭니다 . 이러한 문제 중 하나가 프로젝트에서 오류를 발생시키는 경우 기본 사례는 종종 디버거가 필요하지 않은 오류를 매우 빨리 찾을 수 있도록 도와줍니다! FOO
BAR
아니.
기본 조치가 없으면 상황이 중요합니다. 몇 가지 가치에 대해서만 행동한다면?
게임에서 키 누르기를 읽는 예를 들어보십시오.
switch(a)
{
case 'w':
// Move Up
break;
case 's':
// Move Down
break;
case 'a':
// Move Left
break;
case 'd':
// Move Right
break;
}
첨가:
default: // Do nothing
시간 낭비 일 뿐이며 이유없이 코드의 복잡성을 증가시킵니다.
// do nothing
주석 으로 기본 절을 추가 하면 이것이 'OK'가 아닌 다른 스위치 명령문과 달리 모든 경우에 적용되지 않는 경우 'OK'임을 분명히 알 수 있습니다.
기본 상황이없는 것은 실제로 어떤 상황에서는 유리할 수 있습니다.
스위치 케이스가 열거 형 값인 경우 기본 케이스를 사용하지 않으면 케이스가없는 경우 컴파일러 경고가 표시 될 수 있습니다. 이렇게하면 나중에 새로운 열거 형 값이 추가되고 스위치에서 이러한 값에 대한 사례를 추가하는 것을 잊어 버린 경우 컴파일 타임에 문제에 대해 알 수 있습니다. 유효하지 않은 값이 열거 형 유형으로 캐스팅 된 경우 코드에서 처리되지 않은 값에 대해 적절한 조치를 취해야합니다. 따라서 이것은 간단한 경우에 가장 잘 작동 할 수 있습니다.
enum SomeEnum
{
ENUM_1,
ENUM_2,
// More ENUM values may be added in future
};
int foo(SomeEnum value)
{
switch (value)
{
case ENUM_1:
return 1;
case ENUM_2:
return 2;
}
// handle invalid values here
return 0;
}
어떤 언어로 작업하든 항상 기본 절을 사용합니다.
일이 잘못 될 수 있습니다. 가치는 당신이 기대하는 것과 같지 않을 것입니다.
기본 절을 포함하지 않으려면 가능한 값 세트를 알고 있다고 확신합니다. 가능한 값 세트를 알고 있다고 생각되면 값이이 가능한 값 세트를 벗어나는 경우이를 알려고합니다. 확실히 오류입니다.
이것이 항상 기본 절을 사용하고 예를 들어 Java에서 오류를 발생시키는 이유입니다.
switch (myVar) {
case 1: ......; break;
case 2: ......; break;
default: throw new RuntimeException("unreachable");
}
"연결할 수없는"문자열보다 더 많은 정보를 포함시킬 이유가 없습니다. 실제로 발생하는 경우 어쨌든 소스 및 변수 값 등을 확인해야하며 예외 스택 추적에는 해당 행 번호가 포함되므로 예외 메시지에 더 많은 텍스트를 쓰는 데 시간을 낭비 할 필요가 없습니다.
throw new RuntimeException("myVar invalid " + myVar);
디버거를 사용하지 않고 변수를 검사하지 않고도 버그를 파악하고 수정하기에 충분한 정보를 제공 할 수 있기 때문에 선호합니다 .
default:
. 그러나 더 자주는 그렇지 않습니다 myVar
. 열거 된 것 이외의 다른 값을 가질 수 없다고 생각하기 때문에 기본값을 생략합니다 . 그러나 변수가 "가능한"값 이외의 값을 가질 때 실제 생활에서 놀랐던 횟수를 셀 수 없습니다. 이 경우 나중에 다른 오류 (디버깅하기가 더 어렵다) 또는 잘못된 답변 (테스트에서 간과 될 수 있음)을 일으키는 것이 아니라 예외를 즉시 확인하게 된 예외에 대해 감사했습니다.
AssertionError
대신 대신을 사용 하는 것이 좋습니다 . 또한 누군가가 오류를 잡고 삼킬 가능성은 적습니다. RuntimeException
myVar
우리 회사에서는 Avionics and Defense 시장을위한 소프트웨어를 작성하며 스위치 문에있는 모든 경우를 명시 적으로 처리해야하기 때문에 항상 기본 진술을 포함합니다 ( '아무것도하지 않음'이라는 주석 일지라도). 예상치 못한 (또는 불가능하다고 생각하는) 값에서 오작동하거나 단순히 충돌 할 수는 없습니다.
기본 사례가 항상 필요한 것은 아니지만 항상 요구하면 코드 분석기로 쉽게 확인할 수 있습니다.
"switch"문에 항상 기본 절이 포함 되어야합니까 ? 아니요 . 일반적으로 기본값을 포함 해야합니다 .
기본 절을 포함하면 오류 조건을 주장하거나 기본 동작을 제공하는 등의 조치가 필요한 경우에만 의미가 있습니다. 하나의 "그냥 이유"를 포함하는 것은화물 컬트 프로그래밍이며 가치를 제공하지 않습니다. 모든 "if"문에 "else"가 포함되어야한다고 말하는 것과 같은 "스위치"입니다.
이해가되지 않는 간단한 예는 다음과 같습니다.
void PrintSign(int i)
{
switch (Math.Sign(i))
{
case 1:
Console.Write("positive ");
break;
case -1:
Console.Write("negative ");
break;
default: // useless
}
Console.Write("integer");
}
이것은 다음과 같습니다.
void PrintSign(int i)
{
int sgn = Math.Sign(i);
if (sgn == 1)
Console.Write("positive ");
else if (sgn == -1)
Console.Write("negative ");
else // also useless
{
}
Console.Write("integer");
}
default:
그러한 경우에 당신의 가정을 성문화 한다고 생각합니다 . 예를 들어, sgn==0
인쇄 할 때 integer
(긍정적이든 부정적이든) 괜찮 습니까, 아니면 오류입니까? 그 코드를 읽으면 말하기가 어렵습니다. zero
이 경우 not not 을 작성 integer
하고 프로그래머 sgn
가 -1 또는 +1 일 수 있다고 가정했다고 가정합니다 . 이 경우 default:
프로그래머가 가정 오류를 조기에 포착하여 코드를 변경할 수 있습니다.
실제로 필요 하지 않을 때 기본 절을 갖는 것은 방어 프로그래밍입니다. 이것은 일반적으로 너무 많은 오류 처리 코드로 인해 지나치게 복잡한 코드로 이어집니다. 이 오류 처리 및 탐지 코드는 코드의 가독성을 떨어 뜨리고 유지 관리를 어렵게하며 결국 해결하는 것보다 더 많은 버그를 유발합니다.
따라서 기본값에 도달하지 않으면 추가 할 필요가 없다고 생각합니다.
"도달하지 말아야한다"는 소프트웨어의 버그에 도달하면 사용자 입력 등으로 인해 원치 않는 값이 포함 된 값을 테스트해야 함을 의미합니다.
언어에 따라 다르지만 C에서 열거 형 유형을 전환하고 가능한 모든 값을 처리하면 기본 사례를 포함하지 않는 것이 좋습니다. 이렇게하면 나중에 추가 열거 형 태그를 추가하고 스위치에 추가하는 것을 잊어 버린 경우 유능한 컴파일러가 누락 된 사례에 대한 경고를 표시합니다.
gcc -Wall
(유능한 컴파일러의 가장 일반적인 공통 분모) 정렬 스위치에서 처리되지 않은 열거 형에 대한 경고를 제공합니다.
switch 문에 엄격하게 정의 된 레이블 또는 값 집합 만 있다는 것을 알고 있다면이 방법을 사용하여 항상 유효한 결과를 얻을 수 있습니다. 프로그래밍 방식으로 / 논리적으로 표시되는 레이블 위에 기본값을 추가하십시오. 다른 값을 처리하는 데 가장 적합합니다.
switch(ResponseValue)
{
default:
case No:
return false;
case Yes;
return true;
}
default
계속 필요한가요? 아니면 이렇게하면 생략 할 수있는 특수 구문이 허용됩니까?
Atleast는 Java에서 필수는 아닙니다. JLS에 따르면, 최소한 하나의 기본 사례가 존재할 수 있다고한다. 이는 기본 사례가 허용되지 않음을 의미합니다. 때로는 switch 문을 사용하는 컨텍스트에 따라 다릅니다. 예를 들어 Java에서 다음 스위치 블록에는 기본 대 / 소문자가 필요하지 않습니다.
private static void switch1(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
break;
case "Tuesday":
System.out.println("Tuesday");
break;
}
}
그러나 문자열을 반환 할 것으로 예상되는 다음 방법에서는 컴파일 오류를 피하기 위해 기본 사례가 편리합니다.
private static String switch2(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
return name;
case "Tuesday":
System.out.println("Tuesday");
return name;
default:
return name;
}
}
마지막에 return 문을 사용하여 기본 대소 문자를 지정하지 않고 위의 메소드에 대한 컴파일 오류를 피할 수 있지만 기본 대소 문자를 제공하면 더 읽기 쉽습니다.
MISRA C 와 같은 일부 (오래된) 지침에 따르면 다음과 같습니다.
최종 기본 조항의 요구 사항은 방어 프로그래밍입니다. 이 조항은 적절한 조치를 취하거나 조치를 취하지 않은 이유에 대한 적절한 의견을 포함해야합니다.
이 조언은 현재 관련된 기준에 기반하지 않기 때문에 구식입니다. 할란 카 슬러가 말한 눈부신 누락은 다음과 같습니다.
기본 사례를 제외하면 처리되지 않은 사례가 표시 될 때 컴파일러가 선택적으로 경고하거나 실패 할 수 있습니다. 정적 검증은 결국 동적 검사보다 우수하므로 동적 검사가 필요할 때 가치있는 희생이 아닙니다.
Harlan도 시연 한 것처럼 기본 사례와 동등한 기능을 스위치 후에 다시 만들 수 있습니다. 각 사례가 조기에 반환 될 때 사소한 것입니다.
동적 검사의 일반적인 요구는 넓은 의미에서 입력 처리입니다. 값이 프로그램의 제어 범위를 벗어나면 신뢰할 수 없습니다.
이것은 또한 Misra가 극단적 인 방어 프로그래밍 의 관점 을 취하는 곳입니다 . 유효하지 않은 값이 물리적으로 표현 가능한 한, 프로그램의 정확성에 관계없이 확인해야합니다. 하드웨어 오류가있을 때 소프트웨어를 최대한 신뢰할 수 있어야한다면 이치에 맞습니다. 그러나 Ophir Yoktan이 말했듯이 대부분의 소프트웨어는 버그를 다루지 않는 것이 좋습니다. 후자의 관행은 때때로 공격적인 프로그래밍 이라고 불린다 .
예기치 않은 값이 들어 오려면 기본값이 있어야합니다.
그러나 Adrian Smith는 기본 오류 메시지가 완전히 의미가 없다는 것에 동의하지 않습니다. 사용자가 보지 못할 것으로 보지 않은 처리되지 않은 사례가있을 수 있으며 (도달 할 수 없음) "도달 할 수 없음"과 같은 메시지는 전적으로 의미가 없으며 해당 상황에있는 사람을 도울 수 없습니다.
예를 들어, 완전히 의미없는 BSOD를 몇 번이나 경험 했습니까? 아니면 0x352FBB3C32342에서 치명적인 예외가 있습니까?
switch
명령문에 기본 사례 가없는 경우 개발 단계에서 예측할 수없는 특정 시점에 해당 사례가 발생하면 동작을 예측할 수 없습니다. 사례를 포함하는 것이 좋습니다 default
.
switch ( x ){
case 0 : { - - - -}
case 1 : { - - - -}
}
/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */
이러한 관행은 NULL 역 참조 , 메모리 누수 및 기타 유형의 심각한 버그 와 같은 버그를 초래할 수 있습니다 .
예를 들어 각 조건이 포인터를 초기화한다고 가정합니다. 그러나 default
경우가 발생 하고이 경우 초기화하지 않으면 null 포인터 예외가 발생할 가능성이 있습니다. 따라서 default
사소한 경우에도 case 문 을 사용하는 것이 좋습니다 .
위의 Vanwaril에 대한 가장 투표가 많은 답변에 동의하지 않습니다.
모든 코드는 복잡성을 추가합니다. 또한 테스트 및 문서화를 수행해야합니다. 적은 코드를 사용하여 프로그래밍 할 수 있으면 항상 좋습니다. 내 의견은 비 완전 스위치 문에 기본 절을 사용하고 철저한 스위치 문에 기본 절을 사용하지 않는다는 것입니다. 내가 올바르게했는지 확인하기 위해 정적 코드 분석 도구를 사용합니다. 자세한 내용을 살펴 보겠습니다.
비 완료 스위치 설명 : 항상 기본값이 있어야 합니다. 이름에서 알 수 있듯이 가능한 모든 값을 다루지 않는 진술입니다. 예를 들어 정수 값 또는 문자열에 대한 switch 문도 불가능할 수 있습니다. 여기서 Vanwaril의 예를 사용하고 싶습니다 (그가이 예를 사용하여 잘못된 제안을했다고 생각합니다. 여기에서 반대를 진술하기 위해 사용합니다-> 기본 문장 사용).
switch(keystroke)
{
case 'w':
// move up
case 'a':
// move left
case 's':
// move down
case 'd':
// move right
default:
// cover all other values of the non-exhaustive switch statement
}
플레이어는 다른 키를 누를 수 있습니다. 그런 다음 아무것도 할 수 없었습니다 (기본 사례에 주석을 추가하여 코드에 표시 할 수 있음). 예를 들어 화면에 무언가를 인쇄해야합니다. 이 사례는 발생할 수있는 것과 관련이 있습니다.
철저한 스위치 설명 : 이러한 스위치 설명은 가능한 모든 값 (예 : 등급 시스템 유형의 열거에 대한 스위치 설명)을 포괄합니다. 코드를 처음 개발할 때는 모든 값을 쉽게 다룰 수 있습니다. 그러나 우리는 인간이기 때문에 일부를 잊을 수있는 작은 기회가 있습니다. 또한 나중에 열거 형 값을 추가하여 모든 스위치 문을 다시 철저하게 수정해야하면 오류 지옥에 대한 경로가 열립니다. 간단한 솔루션은 정적 코드 분석 도구입니다. 이 도구는 모든 스위치 명령문을 점검하고 완전한지 또는 기본값이 있는지 확인해야합니다. 다음은 완전한 스위치 설명의 예입니다. 먼저 열거 형이 필요합니다.
public enum GradeSystemType {System1To6, SystemAToD, System0To100}
그런 다음이 열거 형의 변수가 필요합니다 GradeSystemType type = ...
. 철저한 switch 문은 다음과 같습니다.
switch(type)
{
case GradeSystemType.System1To6:
// do something
case GradeSystemType.SystemAToD:
// do something
case GradeSystemType.System0To100:
// do something
}
따라서 GradeSystemType
예를 들어 System1To3
정적 코드 분석 도구 를 확장하면 기본 절이없고 switch 문이 완전하지 않다는 것을 감지해야하므로 저장됩니다.
하나만 추가하면됩니다. 우리가 항상 default
절을 사용 하면 정적 코드 분석 도구는 항상 default
절을 감지하므로 전체 또는 비 완전 스위치 문을 감지하지 못할 수 있습니다. 열거 형을 다른 값으로 확장하고 하나의 switch 문에 추가하는 것을 잊어 버린다면 알 수 없으므로 매우 나쁩니다.
나는 이것이 언어마다 다르며 C ++ 경우 열거 형 클래스 유형에 대한 사소한 점이라고 생각 합니다. 전통적인 C 열거보다 더 안전한 것으로 보입니다. 그러나
std :: byte 구현을 보면 다음과 같습니다.
enum class byte : unsigned char {} ;
출처 : https://en.cppreference.com/w/cpp/language/enum
또한 이것을 고려하십시오 :
그렇지 않으면 T가 고정 기본 유형으로 범위가 지정되거나 범위가 지정되지 않은 열거 유형이고, braced-init-list에 하나의 이니셜 라이저가 있고 이니셜 라이저에서 기본 유형으로의 변환이 좁지 않은 경우 및 초기화는 직접 목록 초기화이며 초기화는 초기화자를 기본 유형으로 변환 한 결과로 초기화됩니다.
(C ++ 17부터)
출처 : https://en.cppreference.com/w/cpp/language/list_initialization
열거자가 정의되지 않은 값을 나타내는 열거 클래스의 예입니다. 이러한 이유로 열거 형을 완전히 신뢰할 수 없습니다. 응용 프로그램에 따라 중요 할 수 있습니다.
그러나 @Harlan Kassler가 자신의 게시물에서 말한 것을 정말 좋아하며 어떤 상황에서는 그 전략을 사용하기 시작할 것입니다.
안전하지 않은 열거 클래스의 예 :
enum class Numbers : unsigned
{
One = 1u,
Two = 2u
};
int main()
{
Numbers zero{ 0u };
return 0;
}