사소한 물체를 위해`this`에서 새로운 배치를 호출하는 것이 안전합니까?


20

이 질문이 이미 여러 번 요청되었지만이 특정 사례에 대한 답변을 찾을 수 없습니다.

리소스를 소유하지 않고 빈 소멸자와 기본 생성자가있는 간단한 클래스가 있다고 가정 해 봅시다. 클래스 내 초기화 기능을 가진 소수의 멤버 변수가 있습니다. 그들 중 하나가 아닙니다 const.

deInit손으로 메서드를 작성하지 않고 다시 초기화하고 그러한 클래스의 객체를 거부하고 싶습니다 . 이렇게하는 것이 안전합니까?

void A::deInit()
{
  new (this)A{};
}

문제가 없습니다. 개체는 항상 유효한 상태이며 this여전히 같은 주소를 가리 킵니다. 그러나 그것은 C ++이므로 확신하고 싶습니다.


2
객체의 구성원이 const입니까?
NathanOliver

2
이것이 유효하면 *this = A{};?
Kevin

2
@Amomum *this = A{};this->operator=(A{});즉, 임시 객체를 만들어에 할당하여 *this모든 데이터 멤버의 값을 임시 값으로 바꾸는 것을 의미합니다. 그것이 당신이 원하는 것이며 (제 의견으로는) 새로운 게재 위치보다 더 읽기 쉽기 때문에 대신에 함께 갈 것입니다.
Kevin

1
@ 케빈 오, 내 나쁜, 당신이 맞아요. 보다- 사본이 없어지면 동일해야 한다고 생각 합니까?
Amomum

1
클래스를 말로 설명하는 대신 하나의 메소드뿐만 아니라 전체 클래스를 작성하는 것이 훨씬 좋습니다. sscce.org
BЈовић

답변:


17

의 합법성과 마찬가지로 delete this, 새로운 배치도 this내가 아는 한 허용됩니다. 또한, 여부에 관한this 나중에 또는 기타 기존 포인터 / 참조를 사용할 수 하여 몇 가지 제한 사항이 있습니다.

[기본 생활]

객체의 수명이 종료 된 후 객체가 차지한 스토리지를 재사용하거나 해제하기 전에 원래 객체가 차지한 저장 위치에 원래 객체를 가리키는 포인터 인 새 객체가 생성됩니다. 원본 객체를 참조하거나 원본 객체의 이름이 자동으로 새 객체를 참조하고 새 객체의 수명이 시작되면 다음과 같은 경우 새 객체를 조작하는 데 사용할 수 있습니다.

  • 새 객체의 저장소는 원래 객체가 차지한 저장소 위치를 정확하게 오버레이합니다.
  • 새 객체는 원래 객체와 동일한 유형이며 (최상위 cv 한정자를 무시하고)
  • 원본 객체의 유형이 한정되지 않고 클래스 유형 인 경우 유형이 한정된 데이터 유형이거나 참조 유형 인 비 정적 데이터 멤버를 포함하지 않는 경우
  • 원본 객체 나 새 객체 모두 잠재적으로 겹치는 하위 객체 ([intro.object])가 아닙니다.

이 예에서는 처음 두 개를 만족하지만 마지막 두 개를 고려해야합니다.

세 번째 요점과 관련하여 함수가 상수가 아닌 것으로 가정하면 원래 객체가 상수가 아니라고 가정하는 것이 안전합니다. 불일치가 발생하면 결함이 호출자 측에 있습니다. const / reference 멤버에 관해서는 이것이 할당 가능하다는 것을 주장함으로써 확인할 수 있다고 생각합니다.

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

물론 할당 가능성은 필수이므로 *this = {};동일한 프로그램을 생성 할 것으로 예상되는 것을 사용할 수 있습니다. 아마도 더 흥미로운 유스 케이스 *this는 다른 유형의 객체에 대한 메모리를 재사용 하는 것입니다.this 적어도 재 해석 + 세탁하지 않고을 ).

와 유사하게 delete this새로운 게재 위치 this는 '안전'한 것으로 거의 설명 할 수 없습니다.


흥미 롭군 일부 유형 특성에서이 모든 조건이 static_assert로 만족되는지 확인할 수 있습니까? const 멤버에 대한 정보가 있는지 확실하지 않습니다 ...
Amomum

1
@Amomum 나는 하위 객체가되는 것이 테스트 될 수 있다고 생각하지 않습니다. const 또는 reference 멤버는 클래스를 할당 할 수 없게 만들므로 사소한 클래스에서는 다르게 발생할 수 있다고 생각하지 않습니다.
eerorika

이것은 엄격 성 앨리어싱이 constness와 관련하여 작동한다는 것을 의미합니까? 이것이 작동하는 예를 제공 할 수 있습니까?
darune

1
@darune const 객체를 생성하고 새로 배치하고 객체의 원래 이름 (또는 기존 포인터 또는 참조)을 사용하면 동작이 정의되지 않습니다. 완전히 동일하지는 않지만 엄격한 앨리어싱 최적화와 거의 동일합니다.
eerorika

1
의 반대는 delete ptr입니다 new T(). 의 반대는 new(ptr)T{}입니다 ptr->~T();. stackoverflow.com/a/8918942/845092
Mooing Duck

7

이를 다루는 규칙은 [basic.life] / 5에 있습니다.

프로그램은 객체가 차지하는 스토리지를 재사용하거나 클래스 유형의 객체에 대해 소멸자를 명시 적으로 호출하여 객체의 수명을 종료 할 수 있습니다. 클래스 유형의 오브젝트의 경우, 오브젝트가 차지하는 저장 영역을 재사용하거나 해제하기 전에 프로그램이 소멸자를 명시 적으로 호출하지 않아도됩니다. 그러나 소멸자를 명시 적으로 호출하지 않거나 delete-expression을 사용하여 스토리지를 해제하지 않으면 소멸자가 암시 적으로 호출되지 않으며 소멸자가 생성 한 부작용에 의존하는 프로그램에 정의되지 않은 동작이 있습니다.

그리고 [basic.life] / 8

객체의 수명이 종료 된 후 객체가 차지한 스토리지를 재사용하거나 해제하기 전에 원래 객체가 차지한 저장 위치에 원래 객체를 가리키는 포인터 인 새 객체가 생성됩니다. 원본 객체를 참조하거나 원본 객체의 이름이 자동으로 새 객체를 참조하고 새 객체의 수명이 시작되면 다음과 같은 경우 새 객체를 조작하는 데 사용할 수 있습니다.

  • 새 객체의 저장소는 원래 객체가 차지한 저장소 위치를 정확하게 오버레이합니다.

  • 새 객체는 원래 객체와 동일한 유형이며 (최상위 cv 한정자를 무시하고)

  • 원본 객체의 유형이 한정되지 않고 클래스 유형 인 경우 유형이 한정된 데이터 유형이거나 참조 유형 인 비 정적 데이터 멤버를 포함하지 않는 경우

  • 원본 객체 나 새 객체 모두 잠재적으로 겹치는 하위 객체 ([intro.object])가 아닙니다.

객체가 사소한 것이기 때문에 [basic.life] / 5에 대해 걱정할 필요가 없으며 [basic.life] / 8의 글 머리 기호를 만족하는 한 안전합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.