문제

로에서 설명 이 답변, copy-and-swap 관용은 다음과 같이 구현됩니다.

class MyClass
{
private:
    BigClass data;
    UnmovableClass *dataPtr;

public:
    MyClass()
      : data(), dataPtr(new UnmovableClass) { }
    MyClass(const MyClass& other)
      : data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
    MyClass(MyClass&& other)
      : data(std::move(other.data)), dataPtr(other.dataPtr)
    { other.dataPtr= nullptr; }

    ~MyClass() { delete dataPtr; }

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, other.data);
        swap(first.dataPtr, other.dataPtr);
    }

    MyClass& operator=(MyClass other)
    {
        swap(*this, other);
        return *this;
    }
};

의 값을 갖는의 MyClass 으로 매개변수에 대한 연산자=매개 변수를 구성할 수 있음을 알 수 있습니다 중 하나를 복사본을 생성자 또는 이동 생성자입니다.할 수 있습니다 다음을 안전하게 데이터를 추출에서 매개 변수입니다.이 방지 코드 중복 지원에서는 예외가 안전합니다.

응답을 언급할 수 있습 교환 또는 이동 변수에서 일시적입니다.그것은 주로 교환에 대해 설명합.그러나,교환하지 않을 경우,최적화,컴파일러에 의해 포함한 세 가지 이동 작업,그리고 더 복잡한 경우는 추가 작동합니다.을 때 당신이 원하는 모든이, 이동 임시로 할당 개체.

이것을 고려 더 복잡한 예 포함, 관 패턴.이 예제에서는 내가 쓴 할당 운전자 코드의 수 있습니다.강조점은 이동 생성자를 할당 운영자와 교환방법:

class MyClass : Observable::IObserver
{
private:
    std::shared_ptr<Observable> observable;

public:
    MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
    MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
    ~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}

    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

    friend void swap(MyClass& first, MyClass& second)
    {
        //Checks for nullptr and same observable omitted
            using std::swap;
            swap(first.observable, second.observable);

            second.observable->unregisterObserver(first);
            first.observable->registerObserver(first);
            first.observable->unregisterObserver(second);
            second.observable->registerObserver(second);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }
}

분명히,복제 코드의 일부에서 수동으로 작성 할당 운영자의 그것과 동일한 이동 생성자입니다.를 수행할 수 있습 교환에 할당 운영자와 행동을 것이 바로,그러나 그것은 잠재적으로 수행하는 더 많은 움직임을 수행하는 추가 등록(에 스왑)및 해지(에서 소멸).

그것은 만들 수 없다 훨씬 더 감각을 다시 사용동 생성자의 코드 대신에?

private:
    void performMoveActions(MyClass&& other)
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

public:
    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        performMoveActions(other);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        performMoveActions(other);
    }

저 처럼 이 방법은 적 열등 스왑 접근 방식이다.나는 생각에 맞는 복사-and-swap 관용이 더 나을 것으로의 복사와 이동의 관용구에서는 C++11,또는 놓치지 않았다 중요한 것일까요?

도움이 되었습니까?

해결책 3

이 질문을했기 때문에 오랜 시간이 지났습니다. 그리고 잠시 동안 대답을 알았지 만, 나는 그것에 대한 답을 작성하는 것을 막습니다. 여기

입니다

대답은 아니오입니다. 복사 및 스왑 관용구는 복사 및 이동 관용구가되어서는 안됩니다.

복사 및 스왑의 중요한 부분 (또한 이동 구조 및 스왑이기도 함)은 안전한 정리가있는 할당 연산자를 구현하는 방법입니다. 이전 데이터는 복사본 구성 또는 이동 구성 임시로 스왑됩니다. 작업이 완료되면 임시가 삭제되고 소멸자가 호출됩니다.

스왑 동작은 파괴자를 재사용 할 수 있으므로 지정 연산자에 정리 코드를 작성할 필요가 없습니다.

완료 및 할당 할 정리 동작이 없으면 할당 연산자를 기본값으로 선언하고 복사 및 스왑이 필요하지 않아야합니다.

이동 생성자 자체는 일반적으로 새 객체이기 때문에 정리 동작이 필요하지 않습니다. 일반적인 간단한 접근 방식은 이동 생성자가 기본 생성자를 호출 한 다음 모든 구성원을 Move-from 객체로 스왑하는 것입니다. Moved-From 객체는 Bland 기본 구성 객체와 같습니다.

그러나이 질문의 관찰자 패턴 예제에서는 이전 오브젝트에 대한 참조가 변경 될 필요가 있으므로 실제로 여분의 정리 작업을 수행 해야하는 예외입니다. 일반적으로 관찰자와 관찰자 및 가능한 한 언제든지 참조가 불가능한 참조를 기반으로하는 다른 디자인 구조물을 만드는 것이 좋습니다.

다른 팁

각 특별한 회원에게 부드러운 사랑의 진료를받을 자격이 있으므로 가능한 한 기본값을 사용할 수 있습니다.

class MyClass
{
private:
    BigClass data;
    std::unique_ptr<UnmovableClass> dataPtr;

public:
    MyClass() = default;
    ~MyClass() = default;
    MyClass(const MyClass& other)
        : data(other.data)
        , dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                : nullptr)
        { }
    MyClass& operator=(const MyClass& other)
    {
        if (this != &other)
        {
            data = other.data;
            dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                        : nullptr);
        }
        return *this;
    }
    MyClass(MyClass&&) = default;
    MyClass& operator=(MyClass&&) = default;

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, second.data);
        swap(first.dataPtr, second.dataPtr);
    }
};
.

원하는 경우, 소멸자는 암시 적으로 기본값으로 표시 될 수 있습니다. 이 예제에서는 명시 적으로 정의하거나 기본값으로 모든 것이 필요합니다.

참조 : http://accu.org/content/conf2014/howard_hinnant_accu_2014.pdf

복사 / 스왑 이용자는 성능이 비용이 될 것입니다 (슬라이드 참조). 예를 들어 고성능 / 자주 사용되는 STD :: 유형과 같은 고성능 / 종종 사용되는 이유와 std::vector는 사본 / 스왑을 사용하지 않는지 궁금합니다. 가난한 성능이 그 이유입니다. std::stringBigClass 또는 std::vector (가능성이있는 것처럼 보이는), 가장 좋은 방법은 특별한 구성원에서 특별한 회원을 호출하는 것입니다. 위의 사항은 어떻게하는지입니다.

할당에 대한 강력한 예외 안전이 필요하면 성능 외에도 제공하는 방법에 대한 슬라이드 ( "strong_assign")를 참조하십시오.

첫째로,그것은 일반적으로필을 작성 swap 기능은 C++11 만큼의 클래스가 움직일 수 있습니다.기본 swap 이트를 이동:

void swap(T& left, T& right) {
    T tmp(std::move(left));
    left = std::move(right);
    right = std::move(tmp);
}

그리고 그것의 요소를 교환합니다.

둘째,이를 바탕으로,복-And-Swap 실제로는 여전히:

T& T::operator=(T const& left) {
    using std::swap;
    T tmp(left);
    swap(*this, tmp);
    return *this;
}

// Let's not forget the move-assignment operator to power down the swap.
T& T::operator=(T&&) = default;

나 사본을 스왑(는 이동)또는 이동 스왑(는 이동),그리고 항상 달성에 가까운 최상의 작동 성능을 발휘합니다.이 있는 부부 중복 할당하지만,잘하면 컴파일러를 돌볼 것입니다.

편집: 이를 구현하는 복사 할당 운영자;별도의 이동 할당 운영자는 또한 필요할 수 있지만 기본적으로,그렇지 않으면 스택 오버플로우가 발생(이동 할당 및 스왑 통화 각 기타 무기한).

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