Menu
Coddy logo textTech

mutable 키워드

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 30번째.

여러분은 const 멤버 함수가 어떠한 멤버 변수도 수정할 수 없다는 것을 배웠습니다. 하지만 때로는 캐시나 액세스 카운터와 같이 객체의 논리적 상태에 영향을 주지 않는 멤버를 변경해야 할 때가 있습니다. mutable 키워드는 이러한 const 제한에 대한 예외를 제공합니다.

mutable 멤버는 const 멤버 함수 내에서도 수정될 수 있습니다:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // const 함수에서도 수정 가능
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // accessCount가 mutable이므로 허용됨
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

mutable이 없으면, const 함수인 getData() 내에서 accessCount를 증가시키는 것은 컴파일러 오류를 발생시킵니다. mutable 키워드는 컴파일러에게 "이 멤버는 객체가 const로 간주될 때에도 변경될 수 있습니다"라고 알려줍니다.

mutable의 일반적인 사용 사례로는 계산된 값 캐싱, 액세스 통계 추적, 그리고 뮤텍스(mutexes)와 같은 동기화 프리미티브가 있습니다. 각 경우에 있어, 수정은 객체가 논리적으로 나타내는 바를 변경하지 않습니다. 캐싱된 값은 단지 최적화일 뿐이며, 액세스 카운터는 사용에 관한 메타데이터입니다.

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

mutable은 드물게 사용해야 합니다. 이는 객체의 관찰 가능한 상태에 영향을 미치지 않는 멤버에만 적용되어야 합니다.

challenge icon

챌린지

쉬움

읽기 전용 메서드의 const-correctness(상수성 유지)를 해치지 않으면서 문서가 조회된 횟수를 추적하는 문서 뷰어를 만들어 보겠습니다. 이는 mutable 키워드를 사용하기에 완벽한 사례입니다.

코드를 정리하기 위해 두 개의 파일을 생성합니다:

  • Document.h: 콘텐츠를 저장하고 조회 통계를 비밀리에 추적하는 Document 클래스를 정의합니다. 클래스에 필요한 요소는 다음과 같습니다:
    • Private 멤버: title (string), content (string), 그리고 const 메서드 내에서도 수정할 수 있는 mutable int viewCount
    • 제목과 콘텐츠를 인자로 받아 viewCount0으로 초기화하는 생성자
    • 제목을 반환하는 getTitle() const 메서드
    • viewCount를 증가시키고(mutable이므로 허용됨) 콘텐츠를 반환하는 getContent() const 메서드
    • 콘텐츠가 액세스된 횟수를 반환하는 getViewCount() const 메서드
    • 콘텐츠의 처음 20글자 뒤에 "..."을 붙여 반환하는 getPreview() const 메서드 — 이것은 단순한 미리보기이므로 조회수를 증가시켜서는 안 됩니다.
  • main.cpp: mutable이 어떻게 메서드의 논리적 상수성을 유지하면서 액세스 통계를 추적할 수 있게 하는지 보여줍니다. 입력으로부터 문서 제목과 콘텐츠를 읽어온 후(각각 별도의 줄), 다음을 수행합니다:
    • 입력받은 값으로 Document 객체를 생성합니다.
    • "Title: <title>"를 출력합니다.
    • "Preview: <preview>"를 출력합니다.
    • "Views after preview: <count>"를 출력합니다.
    • getContent()를 사용하여 전체 콘텐츠에 액세스하고 "Content: <content>"를 출력합니다.
    • "Views after first read: <count>"를 출력합니다.
    • 콘텐츠에 다시 액세스하고 "Content: <content>"를 출력합니다.
    • "Views after second read: <count>"를 출력합니다.
    • "[Const Access] <title>: <content>"를 출력하는 헬퍼 함수 void displayDocument(const Document& doc)를 만듭니다. 이는 const 메서드가 const 참조와 함께 작동함을 증명합니다.
    • 생성한 문서를 인자로 하여 displayDocument()를 호출합니다.
    • "Final view count: <count>"를 출력합니다.

조회수는 미리보기 후에는 0이어야 하며(미리보기는 전체 조회로 간주하지 않음), getContent()가 호출될 때마다 증가해야 합니다. mutable 키워드가 이를 가능하게 합니다. 이 키워드가 없다면 getContent()와 같은 const 메서드 내부에서 viewCount를 수정할 수 없습니다.

콘텐츠가 20자보다 짧은 경우, getPreview()는 전체 콘텐츠 뒤에 "..."을 붙여서 반환해야 합니다.

치트 시트

mutable 키워드는 const 멤버 함수 내에서도 멤버 변수를 수정할 수 있도록 허용합니다:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // const 함수에서도 수정 가능
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // accessCount가 mutable이므로 허용됨
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

mutable이 없으면 const 함수 내에서 멤버 변수를 수정할 경우 컴파일러 오류가 발생합니다. 이 키워드는 객체가 const로 간주될 때도 해당 멤버의 변경이 허용됨을 컴파일러에 알립니다.

일반적인 사용 사례로는 계산된 값 캐싱, 액세스 통계 추적, 동기화 프리미티브 등이 있습니다. 이러한 수정은 객체가 논리적으로 나타내는 내용을 변경해서는 안 됩니다:

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

mutable은 객체의 관찰 가능한 상태에 영향을 주지 않는 멤버에 대해서만 드물게 사용하십시오.

직접 해보기

#include <iostream>
#include <string>
#include "Document.h"
using namespace std;

// TODO: displayDocument 함수 구현하기
// "[Const Access] <title>: <content>"를 출력해야 합니다.
void displayDocument(const Document& doc) {
    // TODO: 이 함수를 구현하세요
}

int main() {
    // 입력 읽기
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: 입력받은 값으로 Document 생성하기
    
    // TODO: "Title: <title>" 출력하기
    
    // TODO: "Preview: <preview>" 출력하기
    
    // TODO: "Views after preview: <count>" 출력하기
    
    // TODO: 전체 내용에 접근하여 "Content: <content>" 출력하기
    
    // TODO: "Views after first read: <count>" 출력하기
    
    // TODO: 내용에 다시 접근하여 "Content: <content>" 출력하기
    
    // TODO: "Views after second read: <count>" 출력하기
    
    // TODO: 생성한 document로 displayDocument() 호출하기
    
    // TODO: "Final view count: <count>" 출력하기
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨