2016-06-20 4 views
4

리소스를 보유하는 클래스를 작성할 때 간단한 스왑 함수를 작성하여 리소스를 전송/복사하는 프로세스를 단순화하는 데 익숙합니다. ,언제 교환 기능이 필요합니까?

class MyArray { 
    public: 
    MyArray(size_t size) : 
    _array(size) 
    { 
    } 

    int & operator[](size_t size) { 
     if(index >= size()) throw std::exception("Index Out of Bounds!"); 
     return _array[index]; 
    } 

    const int & operator[](size_t index) const { 
     if(index >= size()) throw std::exception("Index Out of Bounds!"); 
     return _array[index]; 
    } 

    size_t size() const { 
     return _array.size(); 
    } 

private: 
    std::vector<int> _array; 
}; 

그리고 두 번째 코드에 swap 기능 기본값 : 내가 [등가]과 같이 동일한 코드를 구현하려면 선택한 수있다, 그러나

class MyArray { 
    public: 
    MyArray(size_t size) : 
    _array(size ? new int[size] : nullptr), 
    _size(size) 
    { 
    } 

    MyArray(const MyArray & mv) : 
    MyArray(mv._size) 
    { 
     if(mv._size) std::copy(mv._array, mv._array + mv._size, _array); 
    } 

    static void friend swap(MyArray & mv1, MyArray & mv2) noexcept { 
     std::swap(mv1._array, mv2._array); 
     std::swap(mv1._size, mv2._size); 
    } 

    MyArray(MyArray && mv) { 
     swap(*this, mv); 
    } 

    MyArray & operator=(MyArray mv) { 
     swap(*this, mv); 
     return *this; 
    } 

    ~MyArray() noexcept { 
     delete[] _array; 
    } 

    int & operator[](size_t index) { 
     if(index >= _size) throw std::exception("Index Out of Bounds!"); 
     return _array[index]; 
    } 

    const int & operator[](size_t index) const { 
     if(index >= _size) throw std::exception("Index Out of Bounds!"); 
     return _array[index]; 
    } 

    size_t size() const { 
     return _size; 
    } 
private: 
    int * _array; 
    size_t _size; 
}; 

: 다음 코드는 사소한 예입니다 정상적인 std::swap 동작 (즉, 첫 번째 코드를 임시로 이동 구성하고 두 번째 코드를 첫 번째 코드로 이동 할당 한 다음 두 번째 코드로 임시 할당을 이동 할당합니다. 첫 번째 코드와 동일해야합니다.

그때이 문제는, 내가 명시 적으로 swap 함수를 정의해야 시나리오가 될 것입니다 무슨 이지만, 특별히 난 그냥 첫 번째 예제 코드에서 같은 코드를 (재사용하기 위해 노력하고있어 시나리오 아니다)? 코드에서 명시 적으로 리소스를 관리하는 것이 없지만 swap 함수의 이점을 객관적으로 얻을 수있는 명백한 시나리오가 있습니까? 필자가 작성한 모든 [move-constructable] 클래스에 대해 swap 함수를 자동 쓰기해야하나요, 아니면 그냥 그렇게하면 코드를 낭비할까요?

auto tmp = std::move(rhs); 
rhs = std::move(lhs); 
lhs = std::move(tmp); 

이 발생하는 3 개 움직임을 유발하고, 존재하는 경쟁 임시 개체가 필요합니다 : 당신이 당신의 자신의 스왑 작업을 정의하지 않으면

+0

스왑과 관련하여 이동 생성자를 정의 할 필요는 없습니다. 사실, const 필드의 경우에도 이것은 불가능합니다. 자신 만의 글을 쓸 수도 있습니다. – lorro

+0

@lorro 코드를 더 간단하게 만들었 기 때문에 * to *를 선택했다는 것을 암시한다고 생각하지 않습니다. – Xirema

+1

'MyArray (MyArray && mv)'는 멤버를'mv'로 바꾸기 전에 멤버를 초기화하지 않으므로'mv'가 유효하지 않은 상태로 남겨져 이동 의미를 위반하게됩니다. 먼저 유효한 공백 상태로 초기화 한 다음 스왑하므로'mv'는 공백 상태로 설정됩니다. 예를 들면 다음과 같습니다 :'MyArray (MyArray && mv) : MyArray (0) {swap (* this, mv); }' –

답변

5

using std::swap; swap(lhs, rhs); 대략적으로 컴파일합니다.

move assign 및 move 구문 연산자에 강력한 예외 보장이있는 경우이 스왑은 약한 예외 보장이 있습니다. move assign 및 move 구조 연산자가 nothrow 인 경우이 스왑도 변경되지 않습니다.

비교해 보았을 때, 멤버 별 스왑은 객체를 비 일관적인 상태로 남겨 둘 수 있습니다. 코드가 이것을 처리 할 수 ​​있다면, 완전한 임시 객체를 생성 할 필요가 없다는 것을 의미합니다. 따라서 부분적으로 스왑 된 구성 요소를 처리 할 수 ​​있고 이동에서 생성 된 전체 임시 개체에 약간의 비용이 발생하면 구성원 단위의 스왑이 더 빠를 수 있습니다.

그러나 모든 것이 고갈되지 않으면 (대체로 스왑/이동) 결과 스왑 작업은 논리적으로 동일합니다.


두 번째 은 실제로 자원을 관리하지 않습니다. 이 코드는 std::vector에 의해 처리되며 처음 코드에서 대부분의 코드를 작성했습니다.

제로 규칙은 리소스를 관리하지 않는 클래스는 복사 또는 이동 할당이나 생성 또는 소멸자를 쓸 필요가 없다는 것입니다. 이러한 클래스는 아마도 스왑도 필요하지 않습니다.

복사/이동 및 삭제를 작성하는 클래스, 자원을 관리하는 클래스는 스왑을 사용하여 복사를 수행 할 수 있습니다. 그렇지 않으면 std::swap 괜찮은 일을합니다.그들이 스왑을 사용합니까, 그리고 스왑 memberwise 단순히 경우

여담으로

,이 쓰기 :

friend auto mytie(MyArray& self) noexcept { 
    return std::tie(self.array, self.size); 
} 

를 한 후 스왑이된다 : 당신의 "반복을 감소

friend void swap(MyArray& lhs, MyArray& rhs) noexcept { 
    std::swap(mytie(lhs), mytie(rhs)); 
} 

을 절대 멤버 이름 "을 1로 더합니다.

+0

게시물의 의미는 원래 텍스트의 단락 (* "두 번째 코드에서 스왑 기능은 기본적으로 기본 'std :: swap'동작으로 기본 설정됩니다 (즉, 첫 번째 코드를 임시 코드로 이동 - 두 번째 코드를 첫 번째 코드에 할당하고 두 번째 코드로 임시 코드를 이동 할당하십시오. 첫 번째 코드와 동일해야합니다. "). 그럴까요? – Xirema

+1

@ Xirema 거의 비슷합니다. 관련된 모든 일이 일어나지 않고 클래스의 임시 인스턴스를 만드는 데 비용이 들지 않으면 동일한 것입니다. 여러분의 클래스가'std :: array , 1000000>'이라고 가정하자 : 스마트 한 요소 - 와이즈 스왑은 백만 개의 원소의 임시 배열을 결코 만들지 않을 것이며, 전체 - 객체'std :: swap' 디폴트 스왑 것입니다. 그 비용이 중요 할 수 있습니다. 비슷하게, throw는'std :: swap'과 엘리먼트 - 와이즈 스왑 사이에 차이를 가져올 수있다. – Yakk

관련 문제