Programing

'mutable'키워드는 const 함수로 변수를 수정하는 것 이외의 다른 목적을 가지고 있습니까?

lottogame 2020. 2. 12. 07:59
반응형

'mutable'키워드는 const 함수로 변수를 수정하는 것 이외의 다른 목적을 가지고 있습니까?


얼마 전에 나는 클래스 멤버 변수를 mutable키워드 로 표시하는 코드를 발견했습니다 . 내가 볼 수있는 한 단순히 const메소드 에서 변수를 수정할 수 있습니다 .

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

이것이이 키워드의 유일한 사용입니까, 아니면 눈을 맞추는 것보다 더 많은 것이 있습니까? 그 후 클래스 에서이 기술을 사용 하여 스레드 안전성 이유로 인해 함수를 잠글 boost::mutex수 있도록 변경 가능으로 표시 const했지만 솔직히 말하면 약간 해킹처럼 느껴집니다.


비트 단위 const와 논리 const를 구분할 수 있습니다. 논리적 const는 잠금 예제와 같이 공용 인터페이스를 통해 볼 수있는 방식으로 객체가 변경되지 않는 경우입니다. 또 다른 예로는 처음 요청할 때 값을 계산하고 결과를 캐시하는 클래스가 있습니다.

c ++ 11 mutable을 람다에서 사용하여 값으로 캡처 한 항목을 수정할 수 있음을 표시 할 수 있으므로 (기본적으로 그렇지 않음) :

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

mutable키워드는 관통하는 방법입니다 const당신이 당신의 객체를 통해 드레이프 베일을. const 참조 또는 객체에 대한 포인터가있는 경우 언제 어떻게 표시되는지를 제외하고 는 해당 객체를 수정할 수 없습니다 mutable.

당신과 함께 const참조 또는 포인터 당신이하는 제약이있다 :

  • 보이는 데이터 멤버에 대해서만 읽기 권한
  • 로 표시된 메소드 만 호출 할 수있는 권한 const.

mutable지금 작성하거나 표시된 일련의 데이터 멤버 수 있도록 예외를 만든다 mutable. 그것은 외부에서 볼 수있는 유일한 차이점입니다.

내부적 const으로 볼 수 있는 메소드는 표시된 데이터 멤버에 쓸 수도 있습니다 mutable. 본질적으로 const 베일은 포괄적으로 관통됩니다. 개념을 mutable파괴하지 않고 const유용한 특수한 경우에만 사용 되도록하는 것은 API 디자이너의 책임입니다. mutable키워드는 이러한 특별한 경우에 적용됩니다 그것 때문에 명확하게 표시 데이터 멤버를하는 데 도움이됩니다.

실제로 const코드베이스 전체에 강박 적으로 사용할 수 있습니다 (기본적으로 const"질병"으로 코드베이스를 "감염"하려는 경우 ). 이 세계에서 포인터와 참조는 const거의 예외가 없으므로 추론하고 이해하기 쉬운 코드를 생성합니다. 흥미로운 탈선을 보려면 "참조 투명성"을 찾아보십시오.

mutable키워드가 없으면 결국 const_cast다양한 유용한 특수 사례 (캐싱, 참조 카운트, 디버그 데이터 등)를 처리하는 데 사용해야 합니다. 불행히도 API 클라이언트 가 사용중인 객체 보호 강제로 const_cast파괴 mutable하기 때문에 훨씬 더 파괴적 입니다. 또한 const 포인터 또는 참조를 사용하면 보이는 멤버에 대한 액세스를 호출하는 훼손되지 않은 쓰기 및 메서드가 허용됩니다. 반대로 API 디자이너는 예외에 대해 세분화 된 제어를 수행해야 하며 일반적으로 이러한 예외는 개인 데이터 에서 작동하는 메소드에 숨겨져 있습니다.constconstconst_castmutableconstconst

(NB는 데이터와 메소드 가시성 을 몇 번 언급 합니다 . 여기에서 논의 된 완전히 다른 유형의 객체 보호 인 public 또는 private 또는 protected로 표시된 멤버에 대해 이야기하고 있습니다 .)


boost :: mutex와 함께 사용하는 것이 바로이 키워드를위한 것입니다. 또 다른 용도는 액세스 속도를 높이기위한 내부 결과 캐싱입니다.

기본적으로 'mutable'은 외부에서 볼 수있는 객체의 상태에 영향을 미치지 않는 모든 클래스 속성에 적용됩니다.

귀하의 질문에있는 샘플 코드에서 done_ 값이 외부 상태에 영향을 미치면 변경 가능 변수가 부적절 할 수 있습니다. 부품.


가변은 const메소드 내에서 특정 속성을 수정 가능한 것으로 표시하기위한 것 입니다. 그것이 유일한 목적입니다. 사용하지 않고 디자인을 변경하면 코드가 더 깨끗하고 읽기 쉽습니다 mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

위의 광기가 가변적 인 것이 아니라면 무엇입니까? 다음은 미묘한 경우입니다. 변경 가능은 객체가 논리적으로 일정하지만 실제로 변경해야하는 경우입니다. 이러한 경우는 거의 없으며 그 사이에 존재합니다.

저자가 제공하는 예로는 캐싱 및 임시 디버깅 변수가 있습니다.


캐시와 같은 내부 상태가 숨겨진 상황에서 유용합니다. 예를 들면 다음과 같습니다.

HashTable 클래스
{
...
공공의:
    문자열 조회 (문자열 키) const
    {
        if (key == lastKey)
            lastValue를 반환;

        문자열 값 = lookupInternal (key);

        lastKey = 키;
        lastValue = 값;

        반환 값;
    }

은밀한:
    변경 가능한 문자열 lastKey, lastValue;
};

그런 다음 const HashTable객체 lookup()가 내부 캐시를 수정하는 방법을 계속 사용할 수 있습니다 .


mutable 그렇지 않으면 상수 함수에서 데이터를 수정할 수 있다고 추측 할 때 존재합니다.

의도는 객체의 내부 상태에 "아무것도하지 않는"함수가있을 수 있으므로 함수를 표시 const하지만 실제로는 영향을 미치지 않는 방식으로 일부 객체 상태를 수정해야 할 수도 있습니다. 기능.

키워드는 컴파일러에 대한 힌트 역할을 할 수 있습니다. 이론적 인 컴파일러는 메모리에 읽기 전용으로 표시된 상수 개체 (전역과 같은)를 배치 할 수 있습니다. mutable작업을 수행하지 않아야한다는 힌트가 있습니다.

변경 가능한 데이터를 선언하고 사용하는 몇 가지 유효한 이유는 다음과 같습니다.

  • 나사산 안전. 를 선언하는 mutable boost::mutex것은 완벽하게 합리적입니다.
  • 통계. 인수의 일부 또는 전부가 주어지면 함수 호출 횟수를 계산합니다.
  • 메모. 값 비싼 답변을 계산 한 다음 다시 계산하지 않고 나중에 참조 할 수 있도록 저장하십시오.

그래, 그게 그거야. 캐시를 구현하여 조회 속도를 높이기 위해 클래스의 상태를 논리적으로 변경 하지 않는 메서드로 수정 된 멤버에 사용합니다 .

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

이제는주의해서 사용해야합니다. 호출자는 const메소드 만 사용 하는 경우 스레드 안전하다고 가정 할 수 있으므로 동시성 문제는 큰 관심사 입니다. 물론 mutable데이터를 수정 해도 개체의 동작이 중요한 방식으로 변경되어서는 안됩니다. 예를 들어 디스크에 작성된 변경 사항이 앱에 즉시 표시 될 것으로 예상되는 경우 내가 제공 한 예를 위반할 수 있습니다 .


Mutable은 클래스 내부에 뮤텍스 또는 잠금과 같은 것을 신호하기 위해 해당 클래스 내에서만 사용되는 변수가있을 때 사용됩니다. 이 변수는 클래스의 동작을 변경하지 않지만 클래스 자체의 스레드 안전성을 구현하기 위해 필요합니다. 따라서 "mutable"이 없으면 "const"함수를 사용할 수 없습니다.이 변수는 외부에서 사용할 수있는 모든 함수에서 변경해야하기 때문입니다. 따라서 const 함수에서도 멤버 변수를 쓰기 가능하도록 만들기 위해 mutable이 도입되었습니다.

지정된 변경 가능 변수는 컴파일러와 판독기 모두에게 안전하고 멤버 변수가 const 멤버 함수 내에서 수정 될 수 있음을 알립니다.


mutable은 주로 클래스의 구현 세부 사항에 사용됩니다. 클래스의 사용자는 그것에 대해 알 필요가 없으므로 "메소드"가 const가 될 수 있다고 생각하는 메소드입니다. 뮤텍스를 변경할 수있는 예는 좋은 표준 예입니다.


C ++의 많은 것들과 마찬가지로, 변경 가능도 되돌릴 수없는 불완전한 프로그래머를 위해 해킹 될 있으며 불변 이 아닌 것으로 표시해서는 안됩니다.


논리적으로 사용자에게 상태가없는 (공개 클래스의 API에 "const"게터가 있어야하지만) 기본 구현 (.cpp의 코드)에는 상태가없는 경우 "mutable"을 사용하십시오.

내가 가장 자주 사용하는 경우는 상태가없는 "일반 오래된 데이터"멤버의 지연 초기화입니다. 즉, 이러한 구성원이 구축 (프로세서) 또는 운반 (메모리) 비용이 많이 들고 개체의 많은 사용자가 요청하지 않는 좁은 경우에 이상적입니다. 이러한 상황에서는 성능을 위해 백엔드에 게으른 구성을 원합니다. 빌드 된 오브젝트의 90 %가 오브젝트를 전혀 빌드 할 필요는 없지만, 공용 소비를위한 올바른 Stateless API를 제시해야합니다.


가변 const은 클래스의 비트 단위 const에서 논리 const로 의미를 변경합니다 .

이는 변경 가능한 멤버가있는 클래스는 더 이상 비트 단위이며 더 이상 실행 파일의 읽기 전용 섹션에 나타나지 않음을 의미합니다.

또한 const멤버 함수가을 사용하지 않고 변경 가능한 멤버를 변경할 수 있도록하여 유형 확인을 수정합니다 const_cast.

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

자세한 내용은 다른 답변을 참조하십시오. 단지 유형에 대한 것이 아니라 컴파일 된 결과에 영향을 미친다는 점을 강조하고 싶습니다.


잘못 설계된 반복자와 같은 경우에 따라 클래스는 카운트 또는 기타 부수적 인 값을 유지해야하며, 이는 클래스의 주요 "상태"에 실제로 영향을 미치지 않습니다. 이것은 가장 자주 변하기 쉬워 사용되는 곳입니다. 변경 가능하지 않으면 디자인의 전체 구성 요소를 희생해야합니다.

그것은 대부분 나에게 해킹처럼 느껴집니다. 매우 적은 상황에서 유용합니다.


고전적인 예 (다른 답변에서 언급했듯이)와 mutable지금까지 사용 된 키워드를 본 유일한 상황 Get은 캐시가 클래스의 데이터 멤버로 구현되지 않고 복잡한 메소드 의 결과를 캐싱하는 것입니다 . 메소드의 정적 변수 (여러 함수 또는 일반 청결도 간의 공유로 인해).

일반적으로 mutable키워드 사용에 대한 대안 은 일반적으로 메소드 또는 const_cast트릭 에서 정적 변수 입니다.

다른 자세한 설명은 여기있습니다 .


const 가상 함수를 재정의하고 해당 함수에서 자식 클래스 멤버 변수를 수정하려는 경우 변경 가능 기능이 유용 할 수 있습니다. 대부분의 경우 기본 클래스의 인터페이스를 변경하지 않으려는 경우 자신 만의 가변 멤버 변수를 사용해야합니다.


mutable 키워드는 클래스 테스트 목적으로 스텁을 작성할 때 매우 유용합니다. const 함수를 스텁 할 수 있으며 스텁에 추가 한 테스트 기능이나 카운터를 늘릴 수 있습니다 (변경 가능). 이것은 스텁 된 클래스의 인터페이스를 그대로 유지합니다.


우리가 변경 가능을 사용하는 가장 좋은 예 중 하나는 딥 카피입니다. 복사 생성자에서 const &obj인수로 보냅니다 . 따라서 생성 된 새 객체는 상수 유형입니다. 새로 생성 된 const 객체의 멤버를 변경하려면 (대부분 변경하지 않지만 드물게 변경 될 수 있음)이를로 선언해야합니다 mutable.

mutable스토리지 클래스는 클래스의 정적이 아닌 비 const 데이터 멤버에서만 사용할 수 있습니다. const로 선언 된 객체의 일부인 경우에도 클래스의 가변 데이터 멤버를 수정할 수 있습니다.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

위의 예에서 xconst로 선언 된 객체의 일부이지만 멤버 변수의 값을 변경할 수 있습니다 . 변수 x가 변경 가능한 것으로 선언 되었기 때문 입니다. 그러나 멤버 변수 값을 수정하려고하면 y컴파일러에서 오류가 발생합니다.


'mutable'이라는 키워드는 실제로 예약 키워드이므로 상수 변수의 값을 변경하는 데 사용됩니다. constsnt의 여러 값을 원할 경우 키워드 mutable을 사용하십시오.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   

참고 URL : https://stackoverflow.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-the-variable-to



반응형