2010-03-19 9 views
3

RAII에서 리소스는 액세스 될 때까지 초기화되지 않습니다. 그러나 많은 액세스 메소드가 상수로 선언됩니다. mutable (비 const) 함수를 호출하여 데이터 멤버를 초기화해야합니다.RAII : const 메서드의 데이터 멤버 초기화

예 : 데이터베이스로부터 불러오는

struct MyClass 
{ 
    int get_value(void) const; 

    private: 
    void load_from_database(void); // Loads the data member from database. 

    int m_value; 
}; 

int 
MyClass :: 
get_value(void) const 
{ 
    static bool value_initialized(false); 
    if (!value_initialized) 
    { 
    // The compiler complains about this call because 
    // the method is non-const and called from a const 
    // method. 
    load_from_database(); 
    } 
    return m_value; 
} 

프리미티브 내 용액으로서 mutable 데이터 멤버를 선언 할 수있다. 나는 다른 방법으로 회원을 바꿀 수 있다고 제안하기 때문에 이것을하지 않을 것입니다.

컴파일러 오류를 제거하기 위해 load_from_database() 문을 캐스팅하는 방법은 무엇입니까?

+1

getXXX 함수의 첫 번째 호출에 다소 시간이 걸릴 수 있음을 사용자에게 알리십시오. 또는 사용자가 성능이 중요한 임계 영역에서 처음으로 해당 함수를 호출 할 수도 있습니다. – smerlin

+3

@Thomas Matthews, Here 's RAII가 실제로 무엇인지 설명하는 링크 http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization – Glen

+0

모든 getXXX 함수를 한 번 호출 할 수있는 추가 init 함수를 제공 할 수 있거나 제공해야합니다 ... 사용자가 원하는 기능을 호출하지 못하게 할 수 있습니다 지연 초기화 – smerlin

답변

20

이것은 RAII가 아닙니다. RAII에서는 생성자에서 초기화하여 문제를 해결할 수 있습니다.

그래서 여기에 사용하는 것은 Lazy입니다. 게으른 초기화 또는 지연 계산을하십시오.

mutable을 사용하지 않으면 상처를 입을 수 있습니다.

물론 당신은 const_cast,하지만 사람이하는 경우 사용할 수 있습니다

static const MyClass Examplar; 

을 그리고 컴파일러는 읽기 전용 메모리에 대한 좋은 후보 결정 하는가? 음,이 경우 const_cast의 효과는 정의되지 않습니다. 기껏해야 아무 일도 일어나지 않습니다.

const_cast 경로를 계속 찾으려면 R Samuel Klatchko으로하십시오.

더 나은 대안이 있다고 생각한 경우 변수를 감쌀 수 있습니다. 자신의 클래스에있는 경우에만 get, setload_from_database의 세 가지 방법 만 사용하면 mutable이 될 것입니다.

+4

이것은 RAII – Glen

+4

이 아니라는 것을 지적하기위한 upvote가 있습니다 ... 그리고 거의 항상 잘못된 const_cast에 반대하기 위해서 – laginimaineb

5

기본적으로 캐싱 메커니즘을 구현하고 있습니다. 개인적으로 나는 캐시 된 데이터를 변경 가능한 것으로 표시하는 것이 좋다고 생각합니다.

+1

더 많은 것은 _mutable_ (members)의 존재에 대한 근거가되는 것 같습니다. – mlvljr

1

여기서 const_cast를 사용하지 마십시오. 문제가 생길 수 있습니다. 이 경우 mutable을 사용하는 것은 문제가되지 않지만, 프로파일 러가 다른 방법을 제안하지 않았다면, 처음에는 호출하는 데 비용이 많이 드는 접근기 방법보다 생성하는 데 비용이 많이 드는 객체를 보게되는 것이 놀랍지 않을 것이라고 생각합니다. .

0

메소드가 (예 : 기본 데이터베이스의 상태를 변경하여) 객체 상태를 변경하는 경우 메소드가 const가 아니어야합니다. 이 경우 const 게터를 호출하기 전에 호출되어야하는 별도의 비 const load 메서드가 있어야합니다.

이 방법은 const_cast이 아니며 mutable이 아니어야하며 잠재적으로 값 비싼 작업을 명시 적으로 지정합니다.

+0

문제의 주제는 변수가 처음으로 액세스되는'지연 초기화 (lazy initialization) '를 사용하여 초기화된다는 것입니다. 이 변수에 대한 모든 향후 액세스는 읽기 전용입니다. 데이터베이스는 변경되지 않고 초기화 순서 만 변경됩니다. –

4

[LOOK MA! 성함 없음!:))]

struct DBValue 
{ 
    int get_value(); 

private: 
    void load_from_database(); 
    int value; 
}; 

struct MyClass 
{ 
    MyClass(): db_value(new DBValue()) {} 
    ~MyClass() { delete db_value; } 

    int get_value() const; 

private: 
    DBValue * const db_value; 
}; 

int MyClass::get_value() const 
{ 
    return db_value->get_value(); // calls void load_from_database() if needed 
} 

아이디어는 집계 개체를 모두 const 및 비 const 메소드를 호출하지만 아무것도 돌연변이가 아니라 정치적으로 올바른 MyClassconst와 방법을 가지고있다 const를 포인터을 통해 .

+0

이것은 본질적으로 Jerry Coffin이 말한 내용이지만 예제 코드를 제공했습니다. –

+0

@Thomas Matthews 사실 Matthieu M.의 마지막 부분에서 영감을 받았습니다. – mlvljr

5

Matthieu가 이미 지적했듯이, 여기서하려는 것은 RAII와 관련이 거의 없습니다. 마찬가지로 constmutable의 조합이 실제로 도움이 될지 의심 스럽습니다. constmutable은 유형을 수정하고 모두 해당 유형의 객체에 대한 액세스에 동일하게 적용합니다.

소량의 코드는 쓰기 권한이 있어야하며 다른 것은 그 값에 대한 읽기 액세스 만 필요합니다. C++ (및 가장 유사한 언어)의 기본 설계를 감안할 때, 올바른 방법은 변수를 자체 클래스로 이동시키는 것입니다. 코드의 소량은 쓰기 액세스가 필요합니다.) 그 클래스. 나머지 세계는 클래스 '인터페이스 (즉, 값을 검색하는 멤버 함수)를 통해 읽기 전용 액세스 권한이 부여됩니다.

내가 게시 한 (아마도 삭제 된) MyClass은 다른 회원이 많은 더 큰 클래스의 일원이 아닌 그 자체로 사용하기 만하면됩니다. 변경해야 할 주요 사항은 1) MyClass에서 lazy_int과 같은 이름으로, 2) (적어도 내 선호도에 따라) get_value()operator int()으로 바꿔야합니다. 예, m_value은 아마도 변경 가능해야하지만, 다른 코드가 값 자체에 액세스 할 수 없기 때문에 다른 코드가 값을 쓰는 것을 허용하지 않습니다.

그런 다음 해당 유형의 개체를 큰 클래스에 포함합니다. 그 외부 클래스의 코드는 operator int() 덕분에 그것을 (읽기 전용으로) int로 처리 할 수는 있지만 클래스가 그렇게 할 수 없기 때문에 작성할 수는 없습니다.

관련 문제