const 객체를 변경하는 것이 정의되지 않은 동작인 경우 생성자와 소멸자는 쓰기 액세스로 어떻게 작동합니까?

StackOverflow https://stackoverflow.com/questions/2271046

문제

C++ 표준에서는 원래 선언된 개체를 수정한다고 말합니다. const 정의되지 않은 동작입니다.그러면 생성자와 소멸자는 어떻게 작동합니까?

class Class {
public:
    Class() { Change(); }
    ~Class() { Change(); }
    void Change() { data = 0; }
private:
    int data;
};

//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior

여기서 생성자와 소멸자는 호출 코드와 정확히 동일한 작업을 수행하지만 개체를 ​​변경할 수 있으며 호출자는 허용되지 않습니다. 정의되지 않은 동작이 발생합니다.

구현 및 표준에 따라 어떻게 작동해야 합니까?

도움이 되었습니까?

해결책

표준에서는 생성자와 소멸자가 다음을 처리하도록 명시적으로 허용합니다. const 사물.12.1/4 "생성자"에서:

생성자를 호출할 수 있습니다. const, volatile 또는 const volatile 물체.... const 그리고 volatile 의미론(7.1.5.1)은 생성 중인 객체에 적용되지 않습니다.이러한 의미 체계는 가장 많이 파생된 개체(1.8)에 대한 생성자가 끝난 후에만 적용됩니다.

그리고 12.4/2 "소멸자":

소멸자는 다음과 같이 호출될 수 있습니다. const, volatile 또는 const volatile 물체.... const 그리고 volatile 의미론(7.1.5.1)은 소멸 중인 객체에 적용되지 않습니다.가장 많이 파생된 개체(1.8)에 대한 소멸자가 시작되면 이러한 의미 체계는 더 이상 적용되지 않습니다.

배경으로 Stroustrup은 "Design and Evolution of C++"(13.3.2 Refinement of the Defintion of C++)에서 다음과 같이 말합니다. const):

전부는 아니지만 일부는 보장하기 위해 const 객체가 읽기 전용 메모리(ROM)에 배치될 수 있기 때문에 생성자가 있는 객체(즉, 런타임 초기화가 필요함)가 ROM에 배치될 수 없다는 규칙을 채택했습니다. const 객체는 할 수 있습니다.

...

선언된 객체 const 생성자가 완료된 후 소멸자가 시작될 때까지 변경 불가능한 것으로 간주됩니다.해당 지점 사이의 개체에 대한 쓰기 결과는 정의되지 않은 것으로 간주됩니다.

원래 디자인할 때 const, 나는 이상적이라고 주장했던 것을 기억합니다 const 생성자가 실행될 때까지 쓰기 가능한 객체이며, 일부 하드웨어 마법에 의해 읽기 전용이 되고, 마지막으로 소멸자에 진입하면 다시 쓰기 가능해집니다.실제로 이런 방식으로 작동하는 태그된 아키텍처를 상상할 수 있습니다.이러한 구현은 누군가가 정의된 객체에 쓸 수 있는 경우 런타임 오류를 발생시킵니다. const.반면에 누군가는 정의되지 않은 객체에 쓸 수 있습니다. const 그것은 다음과 같이 전달되었습니다. const 참조 또는 포인터.두 경우 모두 사용자는 이를 버려야 합니다. const 첫 번째.이 견해가 암시하는 바는 const 원래 정의된 객체의 경우 const 그런 다음 거기에 쓰는 것은 기껏해야 정의되지 않은 반면, 원래 정의되지 않은 객체에 동일한 작업을 수행하는 것은 const 합법적이고 잘 정의되어 있습니다.

이러한 규칙의 개선을 통해 다음의 의미는 다음과 같습니다. const 유형에 생성자가 있는지 여부에 의존하지 않습니다.원칙적으로는 모두 그렇습니다.선언된 모든 객체 const 이제 초기 값을 받은 후 변경되지 않도록 ROM에 배치하고, 코드 세그먼트에 배치하고, 액세스 제어 등으로 보호할 수 있습니다.그러나 현재 시스템은 일반적으로 모든 것을 보호할 수 없기 때문에 그러한 보호가 필요하지 않습니다. const 모든 형태의 부패로부터.

다른 팁

Jerry Coffin이 말한 내용을 자세히 설명하면 다음과 같습니다.표준에서는 객체의 수명 동안 해당 액세스가 발생하는 경우에만 const 객체에 대한 액세스를 정의되지 않은 상태로 만듭니다.

7.1.5.1/4:

변경 가능으로 선언된 클래스 멤버(7.1.1)를 수정할 수 있다는 점을 제외하고, 수명(3.8) 동안 const 객체를 수정하려고 하면 정의되지 않은 동작이 발생합니다.

객체의 수명은 생성자가 완료된 후에만 시작됩니다.

3.8/1:

T 유형 객체의 수명은 다음과 같은 경우에 시작됩니다.

  • 유형 T에 대해 적절한 정렬 및 크기를 갖춘 저장소가 확보되었습니다.
  • T가 중요한 생성자를 포함하는 클래스 유형(12.1)인 경우 생성자 호출이 완료된 것입니다.

표준은 실제로 구현이 어떻게 작동하는지에 대해 많이 말하지 않지만 기본 아이디어는 매우 간단합니다. const 에 적용됩니다 물체, 객체가 저장되는 메모리에 (반드시) (반드시)가 아닙니다. CTOR는 객체를 만드는 것의 일부이므로 CTOR가 돌아올 때까지 (때때로) 물체가 아닙니다. 마찬가지로, DTOR는 물체를 파괴하는 데 참여하기 때문에 더 이상 완전한 객체에서도 작동하지 않습니다.

표준을 무시하면 잘못된 행동으로 이어질 수있는 방법이 있습니다. 다음과 같은 상황을 고려하십시오.

class Value
{
    int value;

public: 
    value(int initial_value = 0)
        : value(initial_value)
    {
    }

    void set(int new_value)
    {
        value = new_value;
    }

    int get() const
    {
        return value;
    }
}

void cheat(const Value &v);

int doit()
{
    const Value v(5);

    cheat(v);

    return v.get();
}

최적화되면 컴파일러는 V가 const이라는 것을 알고있어서 호출을 대체 할 수 있습니다. v.get() ~와 함께 5.

그러나 다른 번역 단위에서 당신은 정의했습니다. cheat() 이와 같이:

void cheat(const Value &cv)
{
     Value &v = const_cast<Value &>(cv);
     v.set(v.get() + 2);
}

따라서 대부분의 플랫폼에서는 이것이 실행되지만 최적화가하는 일에 따라 동작이 바뀔 수 있습니다.

사용자 정의 유형의 콘트 스네스는 내장 유형의 구성과 다릅니다. 사용자 정의 유형과 함께 사용될 때의 콘트니스는 "논리적 콘트 스네스"라고합니다. 컴파일러는 "const"라고 선언 한 멤버 기능 만 Const 객체 (또는 포인터 또는 참조)에서 호출 될 수 있도록 강요합니다. 컴파일러는 일부 읽기 전용 메모리 영역에서 객체를 할당 할 수 없습니다. 비정규 멤버 기능은 객체의 상태를 수정할 수 있어야합니다. Const 멤버 변수가 선언 될 때 회원 함수는 할 수 있어야합니다. mutable).

내장 유형의 경우 컴파일러가 객체를 읽기 전용 메모리로 할당 할 수 있다고 생각합니다 (플랫폼이 그러한 것을 지원하는 경우). 따라서 const를 버리고 변수를 수정하면 런타임 메모리 보호 결함이 발생할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top