함수 매개 변수에 ‘const’사용 사용하는 간단한 변경자를 상상해보십시오. void SetValue(const

얼마나 멀리 가니 const? const필요할 때 기능을 합니까, 아니면 전체 돼지 고기를 가지고 어디에서나 사용합니까? 예를 들어, 단일 부울 매개 변수를 사용하는 간단한 변경자를 상상해보십시오.

void SetValue(const bool b) { my_val_ = b; }

const실제로 유용? 개인적으로 매개 변수를 포함하여 광범위하게 사용하기로 선택했지만이 경우 가치가 있는지 궁금합니다.

또한 const함수 선언의 매개 변수에서 생략 할 수 있지만이를 함수 정의에 포함 시킬 수 있다는 사실에 놀랐습니다 .

.h 파일

void func(int n, long l);

.cpp 파일

void func(const int n, const long l)

이것에 대한 이유가 있습니까? 나에게는 조금 특이한 것 같습니다.



답변

그 이유는 매개 변수의 const가 데이터 사본에서 작업하기 때문에 함수 내에서만 로컬로 적용되기 때문입니다. 이것은 함수 서명이 실제로 동일하다는 것을 의미합니다. 비록 이것을 많이하는 것은 나쁜 스타일 일 것입니다.

개인적으로 참조 및 포인터 매개 변수를 제외하고 const를 사용하지 않는 경향이 있습니다. 복사 된 객체의 경우 함수 내에서 의도를 알리기 때문에 더 안전 할 수 있지만 실제로는 중요하지 않습니다. 실제로 판결 요청입니다. 나는 무언가를 반복 할 때 const_iterator를 사용하는 경향이 있으며 그것을 수정하려고하지 않으므로 참조 유형에 대한 const 정확성이 엄격하게 유지되는 한 각각의 것으로 추측합니다.


답변

“호출자의 객체를 수정하지 않기 때문에 인수가 값으로 전달 될 때 const는 의미가 없습니다.”

잘못된.

코드와 가정을 자체 문서화하는 것입니다.

코드에 많은 사람들이 작업하고 함수가 사소한 것이 아니라면 가능한 한 모든 것을 “const”로 표시해야합니다. 산업 강점 코드를 작성할 때 항상 동료가 가능한 한 길을 찾으려고 노력하는 정신병자라고 생각해야합니다 (특히 미래에 종종 자신이기 때문에).

게다가, 앞서 언급 한 누군가 가 컴파일러가 약간 최적화하는 데 도움이 될 수 있습니다 (긴 샷 임에도 불구하고).


답변

때때로 (너무 자주!) 다른 사람의 C ++ 코드를 풀어야합니다. 그리고 우리는 다른 사람의 C ++ 코드가 거의 정의에 의해 완전한 엉망 이라는 것을 알고 있습니다. 따라서 로컬 데이터 흐름을 해독하기 위해 가장 먼저해야 할 일은 컴파일러가 짖기 시작할 때까지 모든 변수 정의 에 const 를 넣는 것 입니다. 이는 한정자 인수를 의미하는데, 이는 호출자가 초기화 한 멋진 로컬 변수이기 때문입니다.

아, 변수가 기본적 으로 const 였고 비 상수 변수에는 변경 이 필요했습니다 🙂


답변

다음 두 줄은 기능적으로 동일합니다.

int foo (int a);
int foo (const int a);

분명히 두 번째 방법으로 정의 된 경우 a본문에서 수정할 수 는 foo없지만 외부와의 차이는 없습니다.

어디 const정말 참조 또는 포인터를 매개 변수로 편리 온다 :

int foo (const BigStruct &a);
int foo (const BigStruct *a);

이것이 말하는 것은 foo는 복사하지 않고 큰 매개 변수, 아마도 기가 바이트 크기의 데이터 구조를 취할 수 있다는 것입니다. 또한 호출자에게 “Foo는 해당 매개 변수의 내용을 변경하지 않습니다.”라고 말합니다. const 참조를 전달하면 컴파일러가 특정 성능 결정을 내릴 수도 있습니다.

* : 불변성을 없애지 않는 한, 그것은 또 다른 게시물입니다.


답변

여분의 불필요한 const는 API 관점에서 좋지 않습니다.

값에 의해 전달되는 내장형 매개 변수에 대해 불필요한 불필요한 const를 코드에 넣으면 API가 복잡해 지지만 호출자 또는 API 사용자에게 의미있는 약속은하지 않습니다 (구현을 방해 할뿐).

필요하지 않을 때 API에서 너무 많은 ‘const’는 ” 울음 울음 소리 “와 같 으며, 결국 사람들은 ‘const’를 무시하기 시작합니다. 왜냐하면 그것이 어디에나 있고 대부분의 시간을 의미하지 않기 때문입니다.

API의 추가 const에 대한 “reductio ad absurdum”인수는 처음 두 가지 점에서 유용합니다. 더 많은 const 매개 변수가 좋으면 const를 가질 수있는 모든 인수는 const가 있어야합니다. 사실, 그것이 정말로 좋으면 const가 매개 변수의 기본값이되고 매개 변수를 변경하려는 경우에만 “mutable”과 같은 키워드를 원할 것입니다.

우리가 할 수있는 곳에 const를 입력 해 봅시다 :

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

위의 코드 줄을 고려하십시오. 선언은 더 복잡하고 읽기가 길고 읽기 어려울뿐만 아니라 4 개의 ‘const’키워드 중 3 개를 API 사용자가 안전하게 무시할 수 있습니다. 그러나 ‘const’를 추가로 사용하면 두 번째 줄이 위험 할 수 있습니다!

왜?

첫 번째 매개 변수를 빨리 읽지 char * const buffer않으면 전달 된 데이터 버퍼의 메모리가 수정되지 않는다고 생각할 수 있지만 이는 사실이 아닙니다! 불필요한 ‘const’는 스캔하거나 빠르게 읽을 때 API에 대한 위험하고 잘못된 가정을 초래할 수 있습니다 .


불필요한 const는 코드 구현 관점에서도 좋지 않습니다.

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

FLEXIBLE_IMPLEMENTATION이 true가 아닌 경우, API는 아래 첫 번째 방법으로 함수를 구현하지 않을 것으로 “약속”합니다.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

그것은 매우 어리석은 약속입니다. 발신자에게 전혀 혜택을주지 않고 구현을 제한하는 약속을해야하는 이유는 무엇입니까?

두 가지 모두 동일한 기능을 완벽하게 유효하게 구현 한 것이므로 수행 한 모든 작업은 불필요하게 한 손으로 등 뒤로 묶여 있습니다.

게다가, 그것은 쉽게 (그리고 법적으로 우회되는) 매우 얕은 약속입니다.

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

랩퍼 함수를 ​​사용하지 않겠다고 약속했지만 어쨌든 그렇게 구현했습니다. 나쁜 사람이 영화에서 누군가를 죽이지 않겠다고 약속하고 대신에 그를 대신 해달라고 명령하는 것과 같습니다.

그 불필요 한 const는 영화 나쁜 사람의 약속보다 더 가치가 없습니다.


그러나 거짓말하는 능력은 더욱 악화됩니다.

가짜 const를 사용하여 헤더 (선언)와 코드 (정의)의 const를 불일치 할 수 있음을 깨달았습니다. const-happy 옹호자들은 const를 정의에만 넣을 수 있기 때문에 이것이 좋은 것이라고 주장합니다.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

그러나 그 반대는 사실입니다 … 당신은 선언에만 가짜 const를 넣고 정의에서 무시할 수 있습니다. 이것은 API의 불필요한 const를 끔찍한 짓과 끔찍한 거짓말로 만듭니다.이 예제를보십시오.

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

불필요한 불필요한 const는 실제로 변수를 변경하거나 비 const 참조로 변수를 전달하려고 할 때 다른 로컬 사본 또는 래퍼 함수를 ​​사용하도록 강제함으로써 구현 자의 코드를 읽기 어렵게 만듭니다.

이 예를보십시오. 더 읽기 쉬운 것은 무엇입니까? 두 번째 함수의 추가 변수에 대한 유일한 이유는 일부 API 디자이너가 불필요한 const를 던 졌기 때문입니까?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

잘만되면 우리는 여기에서 무언가를 배웠다. 불필요한 const는 API 혼란스러운 눈가리개, 성가신 잔소리, 얕고 의미없는 약속, 불필요한 방해이며 때로는 매우 위험한 실수로 이어집니다.


답변

const는 C ++에서 기본값이었습니다. 이처럼 :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

답변

생계를 위해 C ++을 코딩 할 때 가능한 모든 것을 구성했습니다. const를 사용하면 컴파일러가 도움을 줄 수있는 좋은 방법입니다. 예를 들어, 메소드 리턴 값을 구성하면 다음과 같은 오타를 피할 수 있습니다.

foo() = 42

당신이 의미 할 때 :

foo() == 42

상수가 아닌 참조를 반환하도록 foo ()가 정의 된 경우 :

int& foo() { /* ... */ }

컴파일러는 행복하게 함수 호출에 의해 반환되는 익명의 임시 값을 지정할 수 있습니다. 그것을 const 만들기 :

const int& foo() { /* ... */ }

이 가능성을 제거합니다.