8
//code from https://skillsmatter.com/skillscasts/2188-move-semanticsperfect-forwarding-and-rvalue-references 
class Widget { 
public: 
    Widget(Widget&& rhs) 
     : pds(rhs.pds) // take source’s value 
    { 
     rhs.pds = nullptr; // why?? 
    } 

private: 
    struct DataStructure; 
    DataStructure *pds; 
}; 

rhd.pdsnullptr으로 설정하는 이유를 알 수 없습니다. rhs.pds = nullptr;이동 생성자에서 rvalue reference를 null로 설정해야하는 이유는 무엇입니까?

+3

당신이 그것을 년후 경우 그 포인터를 확보하려고했을 제거 소멸자가 거기에 모두가 좋은 nullptr 가하는 것도 삭제하지 않습니다 개간 된거야? – user2357112

+0

두 개체 구성원이 같은 메모리 위치를 가리키고 있지 않도록 ... – HadeS

+0

링크에 유료 로그인이 필요합니다. :((또한, "스킬 스매쉬 (skill smatter)"와 같은 소리가 들리면 "스킬 넘치는 스킬"을 얻을 수 있습니다.) – Potatoswatter

답변

12

일부 세부 정보가 삭제되었습니다. 특히, 생성자는 DataStructure 객체를 동적으로 할당하고 소멸자는 객체를 할당 해제합니다. 이동 중에 하나의 Widget에서 다른 포인터로 복사 한 경우 Widget 두 포인터는 동일한 DataStructure 개체에 대한 포인터를 갖습니다. 그런 다음 그 물체가 파괴되면 그들은 모두 delete으로 시도합니다. 이렇게하면 정의되지 않은 동작이 발생합니다. 이를 피하기 위해 이동되는 Widget의 내부 포인터는 nullptr으로 설정됩니다.

이동 생성자를 구현할 때의 표준 패턴입니다. 하나의 오브젝트에서 다른 오브젝트로 동적으로 할당 된 일부 오브젝트의 소유권을 이동하려는 경우 원래 오브젝트가 할당 된 오브젝트를 더 이상 소유하지 않도록해야합니다.

도식적으로, 당신은 다른 하나의 Widget에서 DataStructure의 소유권을 이동하고자하는이 상황에 시작 : 당신은 그냥 포인터를 복사 한 경우

┌────────┐  ┌────────┐ 
    │ Widget │  │ Widget │ 
    └───╂────┘  └────────┘ 
     ┃ 
     ▼ 
┌───────────────┐ 
│ DataStructure │ 
└───────────────┘ 

, 당신은 할 것 :

┌────────┐  ┌────────┐ 
    │ Widget │  │ Widget │ 
    └───╂────┘  └───╂────┘ 
     ┗━━━━━━━━┳━━━━━━━┛ 
        ▼ 
     ┌───────────────┐ 
     │ DataStructure │ 
     └───────────────┘ 
당신이 다음 nullptr 원래 Widget 포인터를 설정하면

, 당신은 :

┌────────┐   ┌────────┐ 
    │ Widget │   │ Widget │ 
    └────────┘   └───╂────┘ 
          ┃ 
          ▼ 
        ┌───────────────┐ 
        │ DataStructure │ 
        └───────────────┘ 

소유권이 성공적으로 이전되었으며 정의되지 않은 동작을 일으키지 않고 Widget을 모두 삭제할 수 있습니다.

+0

그래서이 상황에 대해'rhs.pds = nullptr;'과' pds = nullptr''rhs.pds = nullptr;'대신'pds = nullptr'을 쓸 수 있다고 생각합니까? – askque

2

DataStructure 객체 가능성이 Widget에 의해 "소유"하고, 포인터를 재설정하면 Widget이 파괴 될 때 실수로 삭제되는 것을 방지 : 우리가이 줄을 제거하면 무슨 일이 일어날 지

.

개체를 옮기면 개체를 "빈 상태"또는 "기본"상태로 다시 설정하고 포인터를 다시 설정하면 규칙을 따르지 않는 무해한 방법입니다.

1
class Widget { 
    public: 
    Widget(Widget&& rhs) 
     : pds(rhs.pds) // take source’s value 
    { 
     rhs.pds = nullptr; // why?? 
    } 
    ~Widget() {delete pds}; // <== added this line 

private: 
    struct DataStructure; 
    DataStructure *pds; 
}; 

위 클래스에 소멸자가 추가되었습니다.

Widget make_widget() { 
    Widget a; 
    // Do some stuff with it 
    return std::move(a); 
} 

int main { 
    Widget b = make_widget; 
    return 0; 
} 

nullptr 할당을 제거하면 어떻게되는지 설명하려면 위의 방법을 확인하십시오. 위젯 a는 도우미 함수로 작성되어 위젯 b에 할당됩니다.

위젯이 범위를 벗어나서 소멸자가 호출되어 메모리를 할당 해제하고 잘못된 메모리 주소를 가리키는 위젯 b가 남아 있습니다.

당신이 우에 nullptr를 할당하면

는 소멸자라고도하지만, 이후 :

+2

왜 std :: move (a)를 반환할까요?! – Omid

+1

@omid 반환 값 최적화를 금지하려면 :) – juanchopanza

+1

@Blaz, omid와 juanchopanza가'return move (a);를 가리키고 있다는 것은 대개 나쁜 것입니다. 컴파일러 (일반적으로)는 암시 적으로 반환 값을 rvalue로 처리합니다. 그래서 당신은'움직이는 것 '에서 아무것도 얻지 못합니다. 그러나 더 중요한 것은 가능한 추가 최적화 (이동보다 빠른 RVO라고 함)가 있으며 명시 적 '이동'을 수행하면이 최적화가 비활성화된다는 것입니다. 당신이 그것을 맹목적으로한다면, 당신은 속도를 높이는 것보다 더 자주 느리게 할 것입니다. –