단일 행 if 또는 루프에 중괄호 (예 : {})를 사용하는 목적은 무엇입니까?


321

C ++ 강사의 강의 노트를 읽고 다음과 같이 썼습니다.

  1. 들여 쓰기 사용 // 확인
  2. 연산자 우선 순위에 의존하지 않음-항상 괄호 사용 // 확인
  3. 항상 {} 블록을 사용 - 심지어 한 줄 //을 위해 하지 OK , 왜 ???
  4. 비교의 왼쪽에있는 Const 객체 // 확인
  5. > = 0 // 멋진 트릭 인 변수에는 부호없는 것을 사용하십시오.
  6. 삭제 후 포인터를 NULL로 설정-이중 삭제 보호 // 나쁘지 않음

세 번째 기술은 나에게 분명하지 않습니다. 한 줄을 { ... }?

예를 들어,이 이상한 코드를 보자 :

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

그것을 다음으로 대체하십시오 :

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;

첫 번째 버전을 사용하면 어떤 이점이 있습니까?


256
가독성 및 유지 보수성. 'j ++'문 블록이 속하는 문은 확실하지 않으며 if 문과 연결되지 않은 후 코드를 추가하는 것은 확실하지 않습니다.
숲과 나무

27
나는 몇 가지 이유로 항상이 줄에 중괄호 {}를 사용하라는 지시를 받았습니다. 코드를보다 명확하게 읽을 수 있습니다. 또한 6 개월 안에 다른 사람이 코드를 편집해야하므로 명확성이 중요하며 중괄호로 오류가 발생할 가능성이 적습니다. 기술적으로 더 정확한 것은 없으며 좋은 습관 일뿐입니다. 프로젝트에 새로운 사람이 겪을 수있는 수천 줄의 코드가있을 수 있습니다.
RossC

30
나는 이중 삭제를 숨기고 잠재적으로 논리 오류를 숨길 것이기 때문에 6에 동의하지 않습니다.
Luchian Grigore 9

28
# 5 까다로울 수 있습니다-이 루프를 고려하십시오 : for (unsigned i = 100; i >= 0; --i).
Archie

36
Btw는 (i % 2 == 0)모순된다 (2). 당신은 연산자 우선 순위에 의존하고 있으며, 의미는 물론이고 ((i % 2) == 0)보다는 (i % (2 == 0)). 나는 규칙 2를 "유효한 감정이지만 '항상'은 틀렸다"로 분류하고 싶습니다.
Steve Jessop

답변:


509

i증분 할 때도 수정 해 봅시다 j:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

아뇨! 파이썬에서 나오면 괜찮아 보이지만 실제로는 다음과 같습니다.

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

물론 이것은 어리석은 실수이지만 숙련 된 프로그래머조차도 할 수있는 실수입니다.

또 다른 좋은 이유ta.speot.is 's answer 에서 지적합니다 .

내가 생각할 수 있는 세 번째 는 nested입니다 if.

if (cond1)
   if (cond2) 
      doSomething();

이제, 당신이 지금 원하는 가정 doSomethingElse()할 때 cond1(새로운 기능)이 충족되지 않습니다. 그래서:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

이는 elseinner와 연관 되기 때문에 분명히 잘못되었습니다 if.


편집 : 이것은 약간의 관심을 받고 있기 때문에 내 견해를 분명히 할 것입니다. 내가 대답 한 질문은 다음과 같습니다.

첫 번째 버전을 사용하면 어떤 이점이 있습니까?

내가 설명 한 것. 몇 가지 이점이 있습니다. 그러나 "항상"규칙이 항상 적용되는 것은 아닙니다. 전 전적으로지지하지 않습니다

항상 {} 블록을 사용하십시오-한 줄이라도 // 안 좋아, 왜 ???

나는 항상{} 블록을 사용 한다고 말하는 것이 아닙니다 . 충분히 간단한 상태와 행동이라면하지 마십시오. 누군가 나중에 와서 코드를 변경하여 기능을 추가 할 수 있다고 생각되면 그렇게하십시오.


5
@Science_Fiction : True, 그러나 i++ before 를 추가하면 j++두 변수가 사용될 때 여전히 범위 내에있게됩니다.
Mike Seymour

26
이것은 매우 합리적으로 들리지만 편집자가 사용자가 아닌 들여 쓰기를 수행한다는 사실을 무시 i++;하고 루프의 일부가 아님을 즉시 보여주는 방식으로 들여 쓰기합니다 . (과거에는이 합리적인 인수되었을 수 있습니다, 나는 이러한 문제를 본 적이 전에 20 년 소개하지 때문이다...)
제임스 간제

43
@ 제임스 : 그것은 "사실"이 아니라, 당신의 작업 흐름입니다. 그리고 많은 사람들의 작업 흐름이지만 모든 사람의 작업 흐름은 아닙니다. 형식 규칙을 적용하는 WYSIWYG 편집기 (vi / emacs / Visual Studio)의 출력이 아니라 C ++ 소스를 일반 텍스트 파일로 취급하는 것이 반드시 오류 라고 생각하지 않습니다 . 따라서이 규칙은 필요한 것 이상으로 사람들이 실제로 C ++를 편집하기 위해 사용하는 것 이상으로 편집기에 독립적입니다. 따라서 "방어".
Steve Jessop

31
@JamesKanze 모든 사람들이 항상 강력한 IDE에서 작업한다는 가정에 정말로 의존하고 있습니까? 마지막으로 쓴 CI는 Nano였습니다. 그럼에도 불구하고 IDE에서 가장 먼저 꺼야 할 것은 자동 들여 쓰기입니다. IDE는 비선형 워크 플로를 방해 하여 불완전한 정보를 바탕으로 '실수'를 수정하려고 시도하기 때문에 . IDE는 모든 프로그래머의 자연스러운 흐름을 자동 들여 쓰기하는 데별로 좋지 않습니다. 이러한 기능을 사용하는 프로그래머는 자신의 스타일을 IDE에 병합하는 경향이 있습니다. IDE를 하나만 사용하는 경우에는 좋지만 많은 작업을 수행하는 경우에는 그다지 좋지 않습니다.
Rushyo

10
"이것은 어리석은 실수이지만 숙련 된 프로그래머조차도 할 수있는 실수입니다." – 내가 대답 한대로, 나는 그것을 믿지 않습니다. 나는 그것이 실제로 문제를 일으키지 않는 완전히 고안된 사건이라고 생각합니다.
Konrad Rudolph

324

이 코멘트를 사용하지 않는 경우와 실수로 변경 제어 흐름에 매우 쉽게 {하고 }. 예를 들면 다음과 같습니다.

if (condition)
  do_something();
else
  do_something_else();

must_always_do_this();

당신이 주석 경우 do_something_else()한 줄의 코멘트와 함께, 당신은이 될 겁니다 :

if (condition)
  do_something();
else
  //do_something_else();

must_always_do_this();

컴파일되지만 must_always_do_this()항상 호출되는 것은 아닙니다.

우리는 코드베이스에이 문제가 있었는데, 누군가 릴리스 전에 일부 기능을 매우 빠르게 비활성화하기 시작했습니다. 다행히도 우리는 코드 검토에서 그것을 잡았습니다.


3
오 소년 !! must_always_do_this();// do_something_else (); 주석을 달면 실행될 동작이 정의되어 있습니다 .
Mr.Anubis

1
@Supr은 처음 작성된대로 중괄호를 사용하면 올바른 흐름을 깨뜨리기가 어렵다고 말하고 코드를 제대로 괄호로
묶지

20
요 전날 나는 이것에 부딪쳤다. if(debug) \n //print(info);. 기본적으로 전체 라이브러리를 꺼 냈습니다.
케빈

4
Fortunately we caught it in code review.아야! 너무 잘못 들린다. Fortunately we caught it in unit tests.훨씬 나아질 것입니다!
BЈовић

4
@ BЈовић 그러나 코드가 단위 테스트라면 어떨까요? 마음이 흔들린다. (농담입니다. 레거시 앱입니다. 단위 테스트는 없습니다.)
ta.speot.is

59

강사의 역량에 대해서는 의문의 여지가 있습니다. 그의 요점을 고려하면 :

  1. 확인
  2. 누구든지 정말로 쓰거나 읽을 (b*b) - ((4*a)*c)래요? 일부 우선 순위는 분명하고 (또는 있어야 함) 여분의 괄호는 혼란에 추가됩니다. (반면, 필요하지 않다는 것을 알더라도 덜 명확한 경우에 괄호를 사용해야합니다.)
  3. 일종의. 조건부 및 루프의 형식을 지정하는 데 널리 사용되는 두 가지 규칙이 있습니다.
    if (cond) {
        암호;
    }
    
    과:
    if (cond)
    {
        암호;
    }
    
    첫 번째로 나는 그에게 동의 할 것이다. 개구부 {는 그렇게 보이지 않으므로 항상 존재한다고 가정하는 것이 가장 좋습니다. 그러나 두 번째로, 나는 (그리고 내가 함께 일한 대부분의 사람들은) 단일 진술에 중괄호를 생략하는 데 아무런 문제가 없습니다. (물론 들여 쓰기는 체계적이며이 스타일을 일관되게 사용하도록 제공했습니다. (그리고 매우 좋은 프로그래머는 읽기 쉬운 코드를 작성하여 첫 번째 형식을 지정할 때에도 중괄호를 생략했습니다.)
  4. 아니요 . if ( NULL == ptr )가독성을 방해하기에는 못생긴 것들이 있습니다. 비교를 직관적으로 작성하십시오. (많은 경우 오른쪽에 일정한 결과가 나타납니다.) 그의 4 가지는 나쁜 조언입니다. 코드를 부 자연스럽게 만드는 것은 코드를 읽기 어렵게 만듭니다.
  5. 아니요 . int특별한 경우를 제외하고 는 아무것도 없습니다 . 숙련 된 C 및 C ++ 프로그래머에게는 unsigned신호 비트 연산자를 사용합니다. C ++에는 실제 기본 유형 (또는 다른 효과적인 하위 범위 유형)이 없습니다. unsigned승격 규칙으로 인해 숫자 값에는 작동하지 않습니다. 일련 번호와 같이 산술 연산이 의미가없는 숫자 값은 아마도 일 수 있습니다 unsigned. 그러나 잘못된 메시지를 보내기 때문에 반대 의견을 제시합니다. 비트 연산도 의미가 없습니다. 기본 규칙은 정수 유형이 int_unless_이고 다른 유형을 사용해야 하는 중요한 이유가 있다는 것입니다.
  6. 아니요 . 이를 체계적으로 수행하는 것은 오해의 소지가 있으며 실제로 어떤 것도 보호하지 못합니다. 엄격한 OO 코드에서, delete this;(당신은 설정할 수 없습니다 종종 가장 흔한 경우가 thisNULL), 그렇지 않으면, 대부분은 delete나중에 어쨌든 포인터를 액세스 할 수 있도록 소멸자에 있습니다. 그리고 그것을 설정하면 NULL다른 포인터가 떠 다니는 것에 대해 아무것도하지 않습니다. 체계적으로 포인터를 설정하면 NULL잘못된 보안 감각이 생겨 실제로 아무것도 사지 않습니다.

일반적인 참조에서 코드를 확인하십시오. Stroustrup은 예를 들어 첫 번째 규칙을 제외한 모든 규칙을 위반합니다.

다른 강사를 찾으십시오. 실제로 자신이 말하는 것을 아는 사람.


13
숫자 4는 못 생겼지 만 그 목적이 있습니다. if (ptr = NULL)을 방지하려고합니다. 내가 사용한 적이 없다고 생각합니다. 내가 delete this본 것보다 더 흔합니까? 사용 후 포인터를 NULL로 설정하는 것이 YMMV가 아니라 나쁜 일이라고 생각하는 경향이 없습니다. 어쩌면 그것은 단지 나이지만 그의 지침의 대부분은 그렇게 나쁘지 않은 것 같습니다.
Firedragon

16
@Firedragon :로 if (ptr = NULL)작성하지 않으면 대부분의 컴파일러가 경고 합니다 if ((ptr = NULL)). 제임스 캔즈 (James Kanze)와 동의해야합니다 NULL.
Leo

34
@JamesKanze : 나는 당신이 여기에 언급 한 대부분의 내용에 동의하지 않는다고 말해야합니다. 숙련 된 C 및 C ++ 프로그래머에게는 부호없는 신호 비트 연산자를 사용합니다. -나는 전혀 동의하지 않습니다 : 비트 연산자 의 사용은 비트 연산자의 사용을 나타냅니다. 나에게 변수의 사용은 변수가 양수 만 나타내야한다는 프로그래머 unsigned열망 을 나타냅니다 . 부호있는 숫자와 혼합하면 대개 강사가 의도 한 컴파일러 경고가 발생합니다.
구성 요소 10

18
숙련 된 C 및 C ++ 프로그래머 에게는 부호없는 신호를 사용하여 비트 연산자를 사용할 수 없습니다. size_t누구?
ta.speot.is

16
@James Kanze, 목적을 고려하십시오. 숙련 된 프로그래머가 작성한 코드를 학습 예제와 비교합니다. 이러한 규칙은 학생들이 겪는 실수이기 때문에 강사가 제공합니다. 경험을 통해 학생들은 이러한 절대성을 편안하게하거나 무시할 수 있습니다.
Joshua Shane Liberman

46

다른 모든 답변은 강사의 규칙 3을 방어합니다.

내가 당신에게 동의한다고 말합시다. 규칙은 중복 적이고 나는 조언하지 않을 것입니다. 항상 중괄호를 추가하면 이론적으로 오류를 방지 하는 것이 사실입니다 . 다른 한편으로, 나는 실제 생활 에서이 문제를 겪어 본 적이 없습니다 . 다른 답변이 암시하는 것과는 달리, 일단 필요한 경우 중괄호를 추가하는 것을 잊지 않았습니다. 적절한 들여 쓰기를 사용하는 경우, 하나 이상의 명령문이 들여 쓰기되면 중괄호를 추가해야한다는 것이 즉시 명백해집니다.

“Component 10”의 답변은 실제로 이것이 실제로 오류를 일으킬 수있는 유일하게 가능한 사례를 강조합니다. 그러나 정규 표현식을 통해 코드를 교체하면 항상 막대한주의가 필요합니다.

이제 메달의 다른 쪽을 살펴 보자. 항상 중괄호를 사용 하는 것이 불리한가? 다른 대답은 단순히이 점을 무시합니다. 그러나이 있다 단점 : 그것은 수직 화면 공간을 많이 차지, 그리고 당신이 필요 이상으로 스크롤해야 할 의미하기 때문에이 차례로 코드를 읽을 수 있습니다.

처음에 많은 가드 절이있는 함수를 고려하십시오 (예, 다음은 나쁜 C ++ 코드이지만 다른 언어에서는 매우 일반적인 상황입니다).

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
    {
        throw null_ptr_error("a");
    }
    if (b == nullptr)
    {
        throw null_ptr_error("b");
    }
    if (a == b)
    {
        throw logic_error("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

이것은 끔찍한 코드이며 다음이 훨씬 더 읽기 쉽다고 강력하게 주장합니다.

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
        throw null_ptr_error("a");
    if (b == nullptr)
        throw null_ptr_error("b");
    if (a == b)
        throw logic_error("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    a->do_something_with(b);
}

마찬가지로 짧은 중첩 루프는 중괄호를 생략하면 이점이 있습니다.

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
        for (auto j = 0; j < a.h(); ++j)
            c(i, j) = a(i, j) + b(i, j);

    return c;
}

다음과 비교하십시오 :

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
    {
        for (auto j = 0; j < a.h(); ++j)
        {
            c(i, j) = a(i, j) + b(i, j);
        }
    }

    return c;
}

첫 번째 코드는 간결합니다. 두 번째 코드는 부풀어 오른다.

그리고 예, 이것은 이전 줄에 오프닝 버팀대를 놓음으로써 어느 정도 완화 될 수 있습니다 . 하지만 그 것이다 아직 어떤 중괄호가없는 코드보다 덜 읽을 수.

한마디로 : 화면 공간을 차지하는 불필요한 코드를 작성하지 마십시오.


26
불필요하게 화면 공간을 차지하는 코드 작성을 믿지 않는다면 오프닝 브레이스를 자체 라인에 배치 할 필요가 없습니다 . 아마도 지금은 GNU의 거대한 복수에서 빠져 나와야하지만 심각하게는 코드를 세로로 작게 만들거나 그렇지 않을 것입니다. 그리고 그렇게한다면, 코드를 덜 세로로 작게 만들도록 설계된 것을하지 마십시오. 당신이 말한대로, 당신은 여전히 줄 것을 고정하는 데 에도 중복 괄호를 제거합니다. 아니면 if (a == nullptr) { throw null_ptr_error("a"); }한 줄로 쓸 수도 있습니다 .
Steve Jessop

6
@ 스티브는 사실, 나는 당신이 언급 한 바로 그 이유로, 이전 행에 여는 중괄호를 넣습니다. 나는 다른 스타일을 사용하여 차이가 얼마나 극단인지 더 분명하게 만들었습니다.
Konrad Rudolph

9
+1 첫 번째 예제는 중괄호없이 읽기가 훨씬 쉽다는 데 완전히 동의합니다. 두 번째 예에서 내 개인 코딩 스타일은 내부가 아닌 외부 for 루프에 중괄호를 사용하는 것입니다. 나는 수직으로 컴팩트 한 코드에 대해 극단적이거나 다른 것에 대해 @SteveJessop에 동의하지 않습니다. 수직 공간을 줄이기 위해 하나의 라이너로 여분의 괄호를 생략하지만 괄호가 정렬 될 때 스코프를 쉽게 볼 수 있기 때문에 여는 괄호를 새 줄에 넣습니다. 목표는 가독성이며, 때로는 더 많은 수직 공간을 사용하고 다른 경우에는 더 적게 사용하는 것을 의미합니다.
Travesty3

22
"실제로이 문제를 경험 한 적이 없습니다": 운이 좋았습니다. 이와 같은 것들은 당신을 태우는 것이 아니라 90 % 3도 화상을줍니다 (그리고 그것은 늦은 밤에 수정을 요구하는 몇 층의 관리입니다).
Richard

9
@Richard 나는 단순히 그것을 사지 않습니다. 채팅에서 설명 했듯이이 오류가 발생하더라도 (아마도 찾을 수는 없지만) 스택 추적을 보면 수정하는 것이 쉽지 않습니다. 코드를 보면 오류의 위치가 분명하기 때문입니다. 과장된 주장은 완전히 근거가 없습니다.
Konrad Rudolph

40

내가 작업중 인 코드베이스는 중괄호로 병적 혐오감을 가진 사람들에 의해 코드로 흩어져 있으며, 나중에 오는 사람들에게는 실제로 유지 보수성과 차이를 만들 수 있습니다.

가장 자주 문제가되는 예는 다음과 같습니다.

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
    this_looks_like_a_then-statement_but_isn't;

그래서 내가 와서 then-statement를 추가하고 싶을 때, 조심하지 않으면 쉽게 끝낼 수 있습니다.

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
    this_looks_like_a_then-statement_but_isn't;
    i_want_this_to_be_a_then-statement_but_it's_not;
}

그것이 왜 당신은 이제까지 것, 디버깅 몇 혼란 분 ~ 소요 일초 괄호를 추가하고 최소한 당신을 저장할 수있는 점을 감안 하지 감소 - 모호한 옵션으로 이동? 나에게 거짓 경제처럼 보인다.


16
이 예제에서 문제가 잘못된 들여 쓰기와 중괄호가 아닌 너무 긴 줄에 있지 않습니까?
혼자 브라 베스

7
그렇습니다. 그러나 사람들이 다른 지침을 따르고 있다고 가정 할 때 '안전한'설계 / 코딩 지침을 따르는 것 (너무 긴 줄이없는 등)은 문제를 일으키는 것으로 보입니다. 브레이스가 처음부터 시작 되었다면이 상황에서 잘못된 if-block으로 끝나는 것은 불가능합니다.
jam

16
중괄호를 추가하는 방법 ( if(really long...editor){ do_foo;}이 경우를 피하는 데 도움이됩니까? 문제는 여전히 똑같습니다. 개인적으로 필요하지 않을 때는 중괄호를 피하는 것을 선호하지만, 작성하는 데 필요한 시간과는 관련이 없지만 가독성이 떨어집니다) 코드에 추가 된 두 줄로 인해
Grizzly

1
좋은 지적-나는 중괄호를 사용하면 그것들이 합리적인 장소에 놓이게 될 것이라고 가정했지만, 물론 일을 어렵게하기로 결정한 누군가가 당신의 예와 같이 그것들을 인라인으로 배치 할 수 있다고 가정했습니다. 그래도 대부분의 사람들은 그렇지 않을 것이라고 생각합니다.

2
파일을 만질 때 가장 먼저하고 마지막으로하는 일은 자동 형식 버튼을 누르십시오. 이러한 문제를 대부분 제거합니다.
Jonathan Allen

20

내 2c :

들여 쓰기 사용

명백하게

연산자 우선 순위에 의존하지 마십시오-항상 괄호를 사용하십시오

"항상" "항상"이라는 단어는 사용하지 않지만 일반적으로이 규칙이 유용하다는 것을 알 수 있습니다. 일부 언어 (Lisp, Smalltalk)에서는 문제가되지 않습니다.

한 줄이라도 항상 {} 블록을 사용하십시오

나는 그런 일을 한 번도하지 않고 한 가지 문제도 겪지 않았지만 그것이 어떻게 학생들에게 좋은지 알 수 있습니다. 그들이 전에 파이썬을 공부했다면.

비교의 왼쪽에있는 Const 객체

요다 조건? 아니요 괜찮아요. 가독성이 떨어집니다. 코드를 컴파일 할 때 최대 경고 수준을 사용하십시오.

> = 0 인 변수에는 부호없는 것을 사용하십시오.

확인. 재미 있지만 Stroustrup이 동의하지 않는다고 들었습니다.

삭제 후 포인터를 NULL로 설정-이중 삭제 방지

나쁜 충고! 삭제되었거나 존재하지 않는 객체를 가리키는 포인터가 없습니다.


5
마지막 포인트 만 +1합니다. 원시 포인터에는 비즈니스 소유 메모리가 없습니다.
Konrad Rudolph

2
서명되지 않은 사용과 관련하여 Stroustrup뿐만 아니라 K & R (C), Herb Sutter 및 (나는 생각합니다) Scott Meyers. 실제로 C ++의 규칙을 이해 한 사람이 서명되지 않은 것을 사용한다고 주장하는 것을 들어 본 적이 없습니다.
James Kanze

2
@JamesKanze 사실, 같은시기에 Stroustrup의 의견 (2008 년 보스턴 회의)을 들었을 때 Herb Sutter가 그곳에 있었고 Bjarne과 그 자리에 동의하지 않았습니다.
Nemanja Trifunovic

3
" unsigned부러짐" 을 완료하기 만하면 문제 중 하나는 C ++에서 비슷한 크기의 부호있는 유형과 부호없는 유형을 비교할 때 비교 수행 하기 전에 부호없는 유형으로 변환한다는 것입니다. 결과적으로 가치가 변화합니다. 서명 된 것으로 변환하는 것이 반드시 더 나은 것은 아닙니다. 비교는 실제로 "있는 것처럼"두 값이 더 큰 유형으로 변환되어 어느 한 유형의 모든 값을 나타낼 수 있어야합니다.
James Kanze

1
@SteveJessop 함수 반환 컨텍스트에서 가져와야한다고 생각합니다 unsigned. 그는 :-) exp(double)이상의 값 을 반환하는 데 아무런 문제가 없다고 확신 MAX_INT합니다. 그러나 다시 한 번, 실제 문제는 암묵적인 변환입니다. int i = exp( 1e6 );완벽하게 유효한 C ++입니다. Stroustrup은 실제로 한 시점에서 손실이있는 암시 적 변환을 더 이상 사용하지 않을 것을 제안했지만위원회는 관심이 없었습니다. (흥미로운 질문 : 것 unsigned-> int손실 간주 나는 둘 다라고 생각 하는데요. unsigned-> intint-> unsigned손실을 만들기 위해 먼 길을 갈 것이다. unsignedOK
제임스 간제를

18

보다 직관적이고 쉽게 이해할 수 있습니다. 의도를 명확하게합니다.

그리고 그것은 새로운 사용자가 모르는 사이를 놓칠 수있는 경우 코드가 침입하지 않도록합니다 {, }새로운 코드 문을 추가하는 동안.


Makes the intent clear+1, 아마도 가장 간결하고 정확한 이유 일 것입니다.
Qix-MONICA가 MISTREATED

13

위의 매우 현명한 제안에 추가하기 위해 이것이 중요하게되는 일부 코드를 리팩토링하는 동안 발생한 한 가지 예는 다음과 같습니다. 한 API에서 다른 API로 전환하도록 매우 큰 코드베이스를 변경하고있었습니다. 첫 번째 API에는 다음과 같이 회사 ID를 설정하라는 호출이있었습니다.

setCompIds( const std::string& compId, const std::string& compSubId );

반면 교체품에는 두 가지 전화가 필요했습니다.

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

나는 매우 성공적인 정규 표현식을 사용하여 이것을 변경하는 것에 대해 설정했습니다. 또한 astyle을 통해 코드를 전달하여 실제로 훨씬 더 읽기 쉽게 만들었습니다. 그런 다음 검토 프로세스를 진행하는 동안 조건부 상황에서 다음과 같이 바뀌고 있음을 발견했습니다.

if ( condition )
   setCompIds( compId, compSubId );

이에:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

그것은 분명히 무엇이 필요한 것이 아닙니다. 나는 블록으로 교체를 완전히 처리 한 다음 구피로 보이는 것을 수동으로 변경하여 처음부터 다시해야했습니다 (적어도 틀리지 않습니다).

나는 astyle 에 옵션 --add-brackets없어서 대괄호를 추가 할 수 있는 옵션 이 없다는 것을 알았습니다.


5
나는 한때 멋진 "Microsoftligent"라는 문서를 보았습니다. 예, 전역 검색 및 바꾸기로 중대한 실수를 저지를 수 있습니다. 즉, 글로벌 검색 및 바꾸기는 Microsoft가 아닌 지능적으로 사용해야합니다.
Pete Becker

4
나는 이것이 사후 사후가 아니라는 것을 알고 있지만, 소스 코드에서 텍스트 교체를 수행하려는 경우 잘 확립 된 종류의 텍스트 교체에 사용하는 것과 동일한 규칙에 따라 수행해야합니다 언어 : 매크로. #define FOO() func1(); \ func2(); 백 슬래시 다음에 줄 바꿈을 사용하여 매크로를 작성해서는 안되며 검색과 교체도 마찬가지입니다. 즉, "항상 괄호 사용"을 스타일 규칙으로 고급으로 사용하여 모든 다중 문 매크로를 래핑하는 것을 방지 할 수 있기 때문입니다 do .. while(0). 그러나 나는 동의하지 않는다.
Steve Jessop

2
Btw, 그것은 일본의 매듭이 잘 확립되었다는 의미에서 "잘 확립되었습니다": 나는 우리가 매크로와 텍스트 대체를 사용하기 위해 우리의 길을 떠나야한다고 말하지는 않지만, 우리가 그렇게 할 때 모든 코드베이스에 특정 스타일 규칙이 성공적으로 적용된 경우에만 작동하는 것이 아니라 작동하는 방식으로 수행해야합니다. –-
Steve Jessop

1
@SteveJessop One은 중괄호와 벨트를 주장 할 수도 있습니다. 그러한 매크로를 사용해야한다면 (그리고 우리가 C ++과, 전에 했었다면 inline) do { ... } while(0)필요한 경우 트릭 (및 많은 추가 괄호)을 사용하여 가능한 한 기능처럼 작동하도록 목표로 삼아야 할 것입니다 . 집 스타일이라면 어디에서나 중괄호를 사용하지 마십시오. (FWIW : 여기에서 논의 된 모든 스타일을 다루는 다양한 집 스타일로 작업했습니다. 심각한 문제는 없었습니다.)
James Kanze

1
그리고 더 많은 스타일을 작업할수록 코드를보다 신중하게 읽고 편집 할 수 있습니다. 따라서 가장 읽기 쉬운 환경 설정이 있더라도 다른 것을 읽을 수 있습니다. 다른 팀이 다른 "하우스 스타일"로 다른 구성 요소를 작성한 회사에서 근무했으며 올바른 해결책은 글로벌 스타일을 만들려고하지 않고 점심 식 사실에서 큰 영향을 미치지 않고 불평하는 것입니다. :-)
Steve Jessop

8

나는 {}명백한 몇 가지 경우를 제외하고 어디에서나 사용 하고 있습니다. 한 줄은 다음 중 하나의 경우입니다.

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

반환하기 전에 몇 가지 방법을 추가하면 손상 될 수 있습니다. 들여 쓰기는 조건이 충족 될 때 리턴이 실행 중임을 나타내지 만 항상 리턴합니다.

statment를 사용하는 C #의 다른 예

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

어느 것이

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}

1
using명세서에 쉼표 만 사용 하면됩니다. :)
Ry-

1
@minitech 그것은 여기서 작동하지 않습니다 – 타입이 같지 않은 타입이 아닌 콤마를 사용할 수 있습니다. Lukas 의이 방법은 정식 방법이며 IDE는이를 다르게 형식화합니다 (두 번째의 자동 들여 쓰기 부족 using).
Konrad Rudolph

8

내가 생각할 수있는 가장 적절한 예는 다음과 같습니다.

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

어떤 if는 것 else과 쌍? 들여 쓰기는 바깥 쪽이을 if얻는다는 것을 의미 else하지만 실제로 컴파일러가 그것을 보는 방법은 아닙니다. 내부 if 을 얻을 것이다 else, 및 외부if 하지 않습니다. 이 코드가 왜 기대에 미치지 못하는지 검사하여 파악하려면이를 알아야합니다 (또는 디버깅 모드에서 이와 같은 방식으로 작동하는 것을보십시오). 파이썬을 안다면 더 혼란스러워집니다. 이 경우 들여 쓰기가 코드 블록을 정의한다는 것을 알고 있으므로 들여 쓰기에 따라 평가 될 것으로 예상합니다. 그러나 C #은 공백에 대한 비행 플립을 제공하지 않습니다.

이제 저는이 "항상 대괄호 사용"규칙에 동의하지 않습니다. 코드를 매우 세로로 시끄럽게하여 빠르게 읽을 수있는 기능을 줄입니다. 진술이 다음과 같은 경우 :

if(someCondition)
   DoSomething();

... 이렇게 이렇게 작성해야합니다. "항상 대괄호 사용"이라는 문장은 "항상 괄호로 수학 연산을 둘러싸고 있습니다"와 같은 소리가납니다. 그것은 매우 간단한 진술을 a * b + c / d로 바꾸어 ((a * b) + (c / d))가까운 친척 (많은 코더의 골칫거리)을 놓칠 가능성을 소개하고 무엇을 위해? 작업 순서는 잘 알려져 있고 적용되므로 괄호는 중복됩니다. a * (b+c) / d예를 들어 일반적으로 적용되는 것과 다른 순서로 작업을 시행하기 위해 괄호 만 사용 합니다. 블록 버팀대는 비슷합니다. 기본값과 다르고 "명백하지 않은"(주관적이지만 일반적으로 꽤 상식적인) 경우에 수행하려는 작업을 정의하는 데 사용하십시오.


3
@AlexBrown ... 정확히 내 요점이었습니다. OP에 명시된 규칙은 "항상 한 줄이라도 대괄호를 사용하는 것"이며, 이는 내가 언급 한 이유에 동의하지 않습니다. 괄호는 코드는 들여 쓰기의 방식으로 작동하지 않기 때문에, 매우 첫 번째 코드 예제에 도움이; 대괄호를 사용 하여 두 else번째 if대신 첫 번째 대괄호를 사용해야 합니다. downvote를 제거하십시오.
KeithS

5

답을 살펴보면 아무도 습관을 들이지 않고 코드의 이야기를 들려주는 일종의 연습을 명시 적으로 언급하지 않았습니다.

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

된다 :

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0) j++;
}

퍼팅 j++같은 줄 다른 사람에게 신호를해야하는 경우로, "나는 오직 증가에이 블록을 원한다 j" . coursethis로, 여기에서 중단 점을 넣고 있기 때문에 라인은 가능한 한 단순한 같은 경우에만 가치가 요정이 언급 매우 유용 될 수 없습니다.

실제로 나는이 코드의 자바에서 '정렬'된 Twitter Storm API의 일부를 실행했습니다 . 이 슬라이드 쇼의 43 페이지 에서 실행 코드의 관련 스 니펫은 다음 과 같습니다 .

...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...

for 루프 블록에는 두 가지가 있으므로 해당 코드를 인라인하지 않습니다. 즉 결코 :

int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;

그것은 끔찍하며 그것이 의도 한대로 작동하는지조차 모릅니다. 하지 마십시오 . 새 줄과 중괄호는 쉼표 나 세미콜론이 산문에서하는 것과 같은 방식으로 분리되지만 관련된 코드 조각을 구별하는 데 도움이됩니다. 위의 블록은 몇 개의 절과 별도의 부분을 구별하기 위해 절대 멈추거나 멈추지 않는 다른 문장이있는 매우 긴 문장입니다.

실제로 다른 사람에게 전화를 걸려면 한 줄로만 작업해야하며 삼항 연산자 나 ?:양식을 사용하십시오.

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

그러나 이것은 코드 골프에 영향을 미치고 있으며 좋은 습관은 아니라고 생각합니다 (j ++을 한쪽에 배치 해야하는지 여부는 명확 :하지 않습니다). NB 이전에 C ++에서 삼항 연산자를 실행하지 않았습니다.이 작동하는지 모르겠지만 존재합니다 .

한마디로 :

독자 (즉, 코드를 관리하는 사람)가 사용자의 스토리 (코드)를 어떻게 해석하는지 상상해보십시오. 가능한 한 명확하게하십시오. 초보자 코더 / 학생이 이것을 유지하고 있다는 것을 알고 있다면, {}가능한 한 많은 사람들 을 혼동하지 않도록하십시오.


1
(1) 같은 줄에 문을 퍼팅은 있습니다 , 읽을 수없는 더. 특히 증분과 같은 간단한 생각은 쉽게 간과됩니다. 음주 새 줄에 넣어 두 었어요. (2) 물론 for루프를 한 줄에 넣을 수 있습니다 . 왜 안되나요? 중괄호를 생략 할 수있는 것과 같은 이유로 작동합니다. 개행은 C ++에서 중요하지 않습니다. (3) 당신의 조건 연산자 예제는 끔찍한 것 외에도 유효하지 않은 C ++입니다.
Konrad Rudolph

@KonradRudolph 덕분에 C ++에서 약간 녹슬 었습니다. 나는 (1) 더 읽기 쉽다고 말하지는 않았지만, 그 코드는 온라인으로 한 줄로 된 것임을 알 수 있습니다. (2) 내 의견은 내가 읽거나 의도 한대로 읽지 못하고 효과가 있다는 것을 알 수 있었다. 이런 이유로하지 말아야 할 일의 예입니다. (3) 고맙습니다. 오랫동안 C ++을 작성하지 않았습니다. 이제 해결하겠습니다.
Pureferret

2
또한 한 줄에 두 개 이상의 식을 넣으면 코드를 디버깅하기가 더 어려워집니다. 그 줄에서 2 차 expresion에 어떻게 중단 점을 두나요?
Piotr Perak

5

이없는 문이 두 개 {}있으면 문제를 놓치기 쉽습니다. 코드가 다음과 같다고 가정 해 봅시다.

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

괜찮아 보인다. 특히 코드를 포함하는 함수가 더 큰 경우 문제가 발생하기 쉽습니다. 문제는 goto fail무조건 실행 된다는 것 입니다. 이것이 얼마나 실망 스러운지를 쉽게 상상할 수 있습니다 ( hash_update모든 것이 제대로 hash_update작동 한 후에 왜 마지막 이 실패 하는지 묻습니다 ).

그러나 이것이 내가 {}모든 곳 을 추가한다는 의미는 아닙니다 (내 의견으로 {}는 모든 곳을 보는 것이 성가시다). 문제가 발생할 수는 있지만 내 개인 코딩 스타일은 조건 {}이 동일한 행에 있지 않을 때 조건문을 금지하므로 내 프로젝트에는 결코 영향 을 미치지 않습니다. 다른 프로젝트에 기여할 때 프로젝트의 코드 스타일을 사용하십시오). 이것은 다음 코드를 잘 만듭니다.

if (something) goto fail;

그러나 다음은 아닙니다.

if (something)
    goto fail;

바로 그거죠. (완전히 불필요한) 줄 바꿈 + 들여 쓰기를 넣지 마십시오.이 문제를 완전히 회피하면 모든 사람이 항상 너무 빨리 일어납니다.
Alexander-복원 모니카

4

루프 및 조건부 블록의 범위를 명확하게 정의하여 코드를 더 읽기 쉽게 만듭니다. 또한 실수로 인한 실수를 방지합니다.


4

wrt 6 : 널 포인터를 삭제하면 아무런 문제가 없으므로 더 안전합니다. 따라서 실수로 해당 경로를 두 번 통과하면 메모리 손상으로 인해 여유 공간이 있거나 다른 것으로 할당 된 메모리가 해제되지 않습니다.

이는 수명이 매우 명확하지 않고 소멸 된 후 다시 생성되는 것으로 알려진 정적 파일 범위 객체 및 싱글 톤의 문제입니다.

대부분의 경우 auto_ptrs를 사용하여 필요를 피할 수 있습니다


6
해당 경로를 두 번 통과하면 프로그래밍 오류가 발생합니다. 이 오류의 위험을 줄이기 위해 포인터를 null로 설정하면 근본적인 문제가 해결되지 않습니다.
Pete Becker

1
동의하지만 이전에이 권장 사항을 보았으며 전문 프로그래밍 표준에 있다고 생각합니다. 나는 더 많은 포스터의 교수가 좋은 때보다는, 그것과 함께 올 한 이유에 대해 언급했다
톰 태너

2
Pete Becker가 말한대로 다음과 같이 : 그것은 근본적인 문제를 해결하지는 않지만 그것을 가릴 수 있습니다. (포인터를 NULL삭제 한 후 포인터를 설정하는 경우 NULL가 있습니다. 포인터가 캐시 된 값을 NULL가리키고 유효하지 않은 캐시 를 가리키는 등의 상황에서 포인터의 값이 올바른 경우 다른 사람이 설정을 볼 때 NULL소멸자의 마지막 줄에 대한 포인터 , 그가 C ++을 알고 있는지 궁금합니다.)
James Kanze

4

나는 Luchian의 대답이 마음에 들었습니다. 사실 그가 옳은 것을 배웠기 때문에 한 줄 블록에도 항상 중괄호를 사용합니다. 그러나 귀하의 예와 같이 필터를 작성할 때 개인적으로 예외를 만듭니다. 이:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

나에게 어수선 해 보인다. 실제로 의도가 단일 조치 인 경우 for 루프와 if 문을 별도의 조치로 분리합니다 .2로 나눌 수있는 모든 정수를 세는 것이 더 표현적인 언어에서는 다음과 같이 작성할 수 있습니다.

j = [1..100].filter(_%2 == 0).Count

클로저가없는 언어에서는 필터를 단일 명령문으로 표현할 수 없지만 for 루프 다음에 if 문이 있어야합니다. 그러나 여전히 프로그래머가 생각하는 한 가지 조치이며 코드에 다음과 같이 반영되어야한다고 생각합니다.

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
  if (i % 2 == 0)
{
    j++;
}

7
나는 모든 사람들이 무시하고 관리하는 방법을 좋아 for (int i = 0; i < 100; i += 2);들여 쓰기에 대한 인수를 계속을 위해, ;-) "가장" "각각의 논리를 표현하는 방법을 우리가 할 수있는 모든 별도의 bunfight, 거기에 아마 i특정 속성을 특정 범위가" 표준 알고리즘의 악몽 조합을 사용하여 루프가없는 C ++에서 filter_iterator및 / 또는 counting_iterator.
Steve Jessop

1
또한, 만약 우리가 그렇게한다면, 우리는 그 결과로 나오는 거대한 단일 진술을 들여 쓰기하는 방법에 동의하지 않을 수 있습니다.
Steve Jessop

2
@Steve, 그것은 단지 예일뿐입니다. 이 패턴은 합법적으로 많이 사용됩니다. 분명히 2로 나눌 수있는 1부터 100까지의 숫자를 세려면 100/2 만하면됩니다.
MikeFHay

2
물론, 나는 그것이 " i특정 속성을 가진 특정 범위의 각각 에 대해"로 추상화 한 이유 입니다. 일반적으로 사람들은 주어진 예에 대한 완전히 다른 접근법을 선호하여 실제 질문을 무시하는 것이 매우 빠릅니다. 그러나 들여 쓰기가 중요 하므로 우리는하지 않습니다 ;-)
Steve Jessop

4

위에서 설명한 오류를 방지하기위한 한 가지 옵션은 중괄호를 사용하지 않을 때 발생하는 작업을 인라인하는 것입니다. 코드를 수정하려고 할 때 오류를 눈치 채기가 훨씬 어렵습니다.

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong

5
두 번째 옵션은와 ( else과) 관련되어 있지 않기 때문에 컴파일 오류가 발생 합니다 if.
Luchian Grigore

인라인에서 눈에 띄지 않는 한 가지 문제는 기본적으로 대부분의 IDE가 자동 포맷 유틸리티를 사용할 때 들여 쓰기 스타일로 변경한다는 것입니다.
혼자 브라 베스

@Honza : 그래도 고비용의 정치적 문제입니다. 코드 기반으로 협력하는 경우 모든 세부 사항까지 동일한 들여 쓰기 스타일을 사용해야하거나 기존 코드를 "단지"자동 형식화하지 않기로 동의해야합니다. 전자의 경우 합의 된 스타일에 여전히이 스타일이 포함될 수 있지만,이를 존중하거나 자동 형식을 사용하지 않도록 IDE를 구성해야합니다. 우리가 모두 동일한 IDE를 영원히 사용한다면 일반적인 형식이 "내 IDE 자동 형식에 관계없이"동의한다는 것은 아주 잘됩니다.
Steve Jessop

3

컴파일러라면 아무런 차이가 없습니다. 둘 다 동일합니다.

그러나 프로그래머에게는 첫 번째가 더 명확하고 읽기 쉽고 오류가 적습니다.


2
{어쨌든 자체 라인을 여는 것 외에 .
Rob

3

중괄호를 추가하는 또 다른 예입니다. 일단 버그를 찾고 있었고 그런 코드를 찾았습니다.

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

메소드를 한 줄씩 읽으면 메소드에 true가 아닌 경우 리턴하는 조건이 있음을 알 수 있습니다. 그러나 실제로는 일부 조건에 따라 일부 변수를 설정하는 100 개의 다른 간단한 이벤트 핸들러처럼 보입니다. 그리고 언젠가 Fast Coder가 들어 와서 메소드 끝에 변수 설정 명령문을 추가합니다.

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

결과적으로 SetAnotherVariableUnconditionnaly는 SomeConditionIsMet () 일 때 실행되지만 모든 행의 크기가 거의 비슷하고 반환 조건이 세로로 들여 쓰기되어 있어도 눈에 띄지 않습니다.

조건부 반환 형식이 다음과 같은 경우 :

if (!SomeConditionIsMet())
{
    return;
}

그것은 매우 눈에 띄고 빠른 코더는 한 눈에 찾을 것입니다.


빠른 코더가 return무언가를 추가하기 전에 함수 본문 내에서 구문 강조된 명령문 을 발견하기 위해 귀찮게 할 수없는 경우 빠른 코더가 코드 근처에 가지 않도록해야합니다. 이러한 사람이 중괄호를 포함하여 코드 주위를 트롤링하는 것을 막지 않을 것입니다.
cmaster-monica reinstate

@cmaster 그는 더 이상 우리와 함께 일하지 않습니다. 어쨌든 구문 강조는 좋지만 명확하게 보지 못하는 사람들이 있다는 것을 기억하십시오 (작년에 맹인 프로그래머의 게시물조차 보았습니다).
Artemix

2

나는 첫 번째 것이 명확하고 두 번째라고 생각합니다. 그것은 작은 코드로, 지침을 닫는의 느낌을 코드가 복잡해 잘입니다 제공 {...}이 경우에도 많은 도움이 endif또는begin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

1

끝났 으면 포인터를 NULL로 설정하는 것이 가장 좋습니다.

이유는 다음과 같습니다.

클래스 A는 다음을 수행합니다.

  1. 메모리 블록 할당
  2. 그런 다음 얼마 후이 메모리 블록을 삭제하지만 포인터를 NULL로 설정하지 않습니다.

클래스 B는 다음을 수행합니다.

  1. 메모리를 할당합니다 (이 경우 클래스 A에 의해 삭제 된 것과 동일한 메모리 블록이 제공됩니다).

이 시점에서 클래스 A와 클래스 B 모두 동일한 메모리 블록을 가리키는 포인터를 가지고 있습니다. 클래스 A와 관련하여이 메모리 블록은 끝나기 때문에 존재하지 않습니다.

다음 문제를 고려하십시오.

클래스 A에 로직 오류가 발생하여 클래스 B에 속하는 메모리에 쓰는 결과는 어떻습니까?

이 특정 인스턴스에서는 메모리 주소가 합법적이므로 클래스 A가 클래스 B 데이터를 효과적으로 손상시키기 때문에 잘못된 액세스 예외 오류가 발생하지 않습니다.

예상치 못한 값이 발생하면 클래스 B가 충돌 할 수 있으며 충돌이 발생하면 문제가 클래스 A에있을 때 클래스 B에서이 버그를 사냥하는 데 오랜 시간이 걸립니다.

삭제 된 메모리 포인터를 NULL로 설정 한 경우 클래스 A의 논리 오류가 NULL 포인터에 쓰려고하면 예외 오류가 발생합니다.

포인터가 두 번째로 NULL 일 때 이중 삭제로 인한 논리 오류가 걱정되는 경우이를 위해 assert를 추가하십시오.

또한 : 투표를 중단하려는 경우 설명하십시오.


2
논리 오류가있는 경우이를 마스킹하는 대신 수정해야합니다.
uınbɐɥs

@Barmar, OP는 말합니다 ... 6. 삭제 후 포인터를 NULL로 설정-이중 삭제 보호 // 나쁘지 않습니다. 일부 사람들은 Null로 설정하지 말고 응답했으며 왜 NULL로 설정해야하는지 말하고 있습니다. 6의 어떤 부분입니까? NULL 설정에 대한 대답이 6에 ​​맞지 않습니까?
John

1
@Shaquin, 그리고 당신은 처음에 이러한 논리 오류를 찾는 데 어떻게 제안합니까? 메모리가 삭제 된 후 포인터 변수를 NULL로 설정하면 NULL 포인터를 참조하려고 시도하면 불법으로 시도한 행에서 디버거가 충돌합니다. 추적하여 논리 오류의 위치를 ​​확인하고 문제점을 해결할 수 있습니다. 메모리를 삭제 한 후 포인터 변수를 NULL로 설정하지 않으면 UNAWARE 논리 오류로 인해이 삭제 된 메모리를 작성하려는 불법적 인 시도가 성공할 수 있으며이 시점에서 충돌하지 않습니다. 마스킹하지 않습니다.
John

1

항상 중괄호를 사용하는 것은 매우 간단하고 강력한 규칙입니다. 그러나 중괄호가 많으면 코드가 우아하지 않을 수 있습니다. 규칙에서 중괄호를 생략 할 수 있으면보다 자세한 스타일 규칙과보다 정교한 도구가 있어야합니다. 그렇지 않으면 혼란스럽고 혼란스러운 (우아하지 않은) 코드가 쉽게 발생할 수 있습니다. 따라서 다른 스타일 가이드 및 도구와 별도로 단일 스타일 규칙을 사용하는 것은 효과가 없습니다. 나는 다른 답변에서 언급되지 않은 규칙 # 3에 대한 중요한 세부 정보를 가져올 것입니다.

첫 번째 흥미로운 세부 사항은 해당 규칙의 대부분의 지지자들이의 경우이를 위반할 것에 동의한다는 것입니다 else. 다시 말해 그들은 그러한 코드를 검토 할 것을 요구하지 않습니다.

// pedantic rule #3
if ( command == Eat )
{
    eat();
}
else
{
    if ( command == Sleep )
    {
        sleep();
    }
    else
    {
        if ( command == Drink )
        {
            drink();
        }
        else
        {
            complain_about_unknown_command();
        }
    }
}

대신, 그들이 그것을 보면 그들은 다음과 같이 작성하도록 제안 할 수도 있습니다.

// not fully conforming to rule #3
if ( command == Eat )
{
    eat();
}
else if ( command == Sleep )
{
    sleep();
}
else if ( command == Drink )
{
    drink();
}
else
{
   complain_about_unknown_command();
}

else와 사이에 중괄호가 없으므로 기술적으로 해당 규칙을 위반하는 것입니다 if. 이러한 규칙의 이중성은 규칙없는 도구를 사용하여 코드베이스에 자동으로 적용하려고 할 때 발생합니다. 실제로 논쟁의 여지가있는 이유는 도구가 자동으로 스타일을 적용하도록하는 것입니다.

두 번째 세부 사항 (이 규칙의 지지자들이 종종 잊어 버리는 경우도 있음)은 발생할 수있는 오류가 해당 규칙 # 3을 위반 한 경우에만 발생하지 않는다는 것입니다. 실제로는 거의 항상 규칙 # 1 위반을 수반합니다 (아무도 논쟁하지 않습니다). 자동 도구의 ​​관점에서 다시 한 번, 규칙 # 1을 위반하면 즉시 불만을 제기하는 도구를 만드는 것이 어렵지 않으므로 대부분의 오류를 적시에 잡을 수 있습니다.

세 번째 세부 사항 (이 규칙의 반대자들이 종종 잊어 버리는 것)은 단일 세미콜론으로 표시되는 빈 명령문의 혼란스러운 특성입니다. 약간의 경험이있는 대부분의 개발자는 단독으로 세미콜론을 잘못 사용하거나 단독 세미콜론을 사용하여 작성된 빈 문장으로 인해 조만간 혼란스러워졌습니다. 단일 세미콜론 대신 두 개의 중괄호가 시각적으로 쉽게 발견됩니다.


1

나는 항상 {}한 줄에 사용하는 것은 아니라는 것을 인정해야 하지만 좋은 연습입니다.

  • 다음과 같이 대괄호없이 코드를 작성한다고 가정 해 보겠습니다.

    for (int i = 0; i <100; ++ i) for (int j = 0; j <100; ++ j) DoSingleStuff ();

그리고 얼마 후에 j다른 루프 를 추가하고 싶을 때 정렬을 통해 브래킷을 추가하는 것을 잊어 버립니다.

  • 메모리 할당 해제가 더 빠릅니다. 범위가 넓고 내부에 큰 배열을 생성한다고 가정 해 봅시다 ( new스택 하지 않고 ). 해당 배열은 범위를 벗어난 직후 메모리에서 제거됩니다. 그러나 해당 배열을 한 곳에서 사용할 수 있으며 잠시 동안 스택 상태가되어 일종의 쓰레기가 될 수 있습니다. 스택이 제한적이고 크기가 작기 때문에 스택 크기를 초과 할 수 있습니다. 그래서 어떤 경우에는 그것을 {}막기 위해 글 을 쓰는 것이 좋습니다 . 참고 이것은 한 줄이 아니라 그러한 상황에 대한 것입니다.

    if (...) {// SomeStuff ... {// 우리는 if, while 등이 없습니다. // SomeOtherStuff} // SomeMoreStuff}

  • 그것을 사용하는 세 번째 방법은 두 번째와 비슷합니다. 스택을 깨끗하게 만드는 것이 아니라 일부 기능 을 여는 것입니다. mutex긴 기능 을 사용하는 경우 일반적으로 데이터에 액세스하기 직전과 읽기 / 쓰기를 마친 직후에 잠금 및 잠금 해제하는 것이 좋습니다. 참고 일부 자신이 있으면이 방법은 사용 class또는 structconstructordestructor잠금 메모리.

  • 더 많은 것 :

    if (...) if (...) SomeStuff (); else SomeOtherStuff (); // 두 번째로 넘어가지만, 악어가 첫 번째임을 보여줍니다 ...

나는 {}한 줄 에 항상 사용하는 가장 좋은 방법은 무엇인지 말할 수는 없지만 그렇게하는 것은 나쁘지 않습니다.

중요 편집 한 줄에 대해 컴파일 코드 괄호를 쓰면 아무 것도 수행하지 않지만 코드가 해석되면 코드 속도가 약간 느려집니다. 아주 약간.


1

제어문을 작성하는 방법에는 여러 가지가 있습니다. 특정 조합은 가독성을 손상시키지 않고 공존 할 수 있지만 다른 조합은 문제를 일으킬 수 있습니다. 스타일

if (condition)
  statement;

제어문을 작성하는 다른 방법 들과는 편안하게 공존 할 수 있지만 다른 방법 들과는 잘 어울리지 않습니다. 여러 줄로 된 제어문이 다음과 같이 작성된 경우 :

if (condition)
{
  statement;
  statement;
}

그러면 어떤 if문장이 한 줄을 제어하고 어떤 문장이 여러 줄을 제어하는지 시각적으로 알 수 있습니다. 그러나 여러 줄로 된 if문장이 다음과 같이 작성된 경우 :

if (condition) {
  statement;
  statement;
}

그러면 if필요한 괄호를 추가하지 않고 단일 문 구조 를 확장하려는 사람이있을 가능성 이 훨씬 높습니다.

if코드베이스가 양식을 많이 사용하는 경우 단일 명령문 다음 행 명령문도 문제가 될 수 있습니다.

if (condition) statement;

저 자신의 선호는 문장을 자체 라인에두면 if비슷한 제어 블록을 가진 문장 이 많은 경우를 제외하고는 일반적으로 가독성을 향상시키는 것입니다.

if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.

이 경우 일반적으로 이러한 if문장 그룹을 빈 줄로 시작하여 다른 코드와 시각적으로 구분합니다. 모두 if동일한 들여 쓰기로 시작하는 다양한 문장을 갖는 것은 비정상적인 것이 있다는 명확한 시각적 표시를 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.