SORU
17 ŞUBAT 2012, Cuma


Atama operatörü taşıma ve `bu ( = ! ve oluşturduğu)`

Bir sınıf atama, genellikle, nesnenin atanan bir çuval inciri berbat değilsin bu yüzden yürütmesini nesne olup olmadığını kontrol etmek gerekir:

Class& Class::operator=(const Class& rhs) {
    if (this != &rhs) {
        // do the assignment
    }

    return *this;
}

Hareket atama operatörü için aynı şey ihtiyacın var mı? Hiç this == &rhs gerçek olacağı bir durum var mı?

? Class::operator=(Class&& rhs) {
    ?
}

CEVAP
17 ŞUBAT 2012, Cuma


Vay be, sadece bitirmem gereken o kadar çok şey var ki...

İlk, Copy and Swap her zaman doğru yolu Kopya Atama uygulamak için değil. Neredeyse kesinlikle dumb_array, bu durumda alt-optimal bir çözümdür.

Copy and Swap kullanımı dumb_array alt katmanında tam özellikleri ile en pahalı operasyon koyarak klasik bir örneğidir. Verdiği özellik isteyen müşteriler için idealdir ve performans cezası ödemek. Ne istediklerini tam olarak aldılar.

Ama verdiği özellik ve yüksek performans arayan yerine, müşteriler için bir felaket. Onlar için dumb_array çok yavaş olduğu için yeniden yazmak zorunda olduklarını, başka bir yazılım. dumb_array farklı dizayn edilmiş, ya da istemci için hiçbir ödün vermeden, hem de müşterileri memnun olabilir.

Hem müşterileri tatmin etmek için anahtarı en hızlı işlemleri en düşük seviyede inşa etmek, ve sonra fuller özellikler için üstüne API eklemek için daha fazla harcama. I. e. sen ödemek için güçlü bir istisna garantisi, ince, gerekir. Buna ihtiyacın yok mu? Burada daha hızlı bir çözüm.

Beton alalım: Burada dumb_array için: hızlı, temel durum garanti Kopya Atama operatör

dumb_array& operator=(const dumb_array& other)
{
    if (this != &other)
    {
        if (mSize != other.mSize)
        {
            delete [] mArray;
            mArray = nullptr;
            mArray = other.mSize ? new int[other.mSize] : nullptr;
            mSize = other.mSize;
        }
        std::copy(other.mArray, other.mArray   mSize, mArray);
    }
    return *this;
}

Açıklama:

Modern donanım üzerinde daha pahalı şeylerden biri öbek için bir yolculuk yapmak. Öbek için bir gezi önlemek için yapabileceğiniz bir şey zaman ve çaba harcanıyor. dumb_array müşterileri de genellikle aynı boyutta diziler atamak isteyebilirsiniz. Ve bunu yaptıklarında, tüm yapmanız gereken memcpy (std::copy altında gizli). Aynı boyutta yeni bir dizi tahsis ve eski aynı boyutta bir zaman ayırması istemiyorum!

Aslında güçlü özel güvenlik isteyen müşterileriniz için:

template <class C>
C&
strong_assign(C& lhs, C rhs)
{
    swap(lhs, rhs);
    return lhs;
}

Ya da belki C 11'de hareket atama Yararlanmak istiyorsanız şu olmalıdır:

template <class C>
C&
strong_assign(C& lhs, C rhs)
{
    lhs = std::move(rhs);
    return lhs;
}

dumb_arraymüşterileri hız değeri s operator= demeliler. Eğer güçlü özel güvenlik ihtiyaçları varsa, nesneleri geniş bir yelpazede üzerinde çalışacak ve bir kez uygulanması gereken sadece bu arayabilirler genel algoritmalar vardır.

Şimdi asıl soruya geri zaman içinde bu noktada türü-o):

Class&
Class::operator=(Class&& rhs)
{
    if (this == &rhs)  // is this check needed?
    {
       // ...
    }
    return *this;
}

Bu aslında tartışmalı bir soru. Bazıları Evet diyecektir, kesinlikle, bazı "hayır" diyecektir.

Benim kişisel görüşüm, bu onay gerekmez.

Gerekçesi:

Bir nesne rvalue başvurusu bağlandığı zaman iki şeyden biridir:

  1. Bir geçici.
  2. Arayanın sen inanmak istediği bir nesnedir geçici.

Eğer gerçek bir geçici bir nesne için bir başvuru varsa, o zaman tanım gereği, bu nesne için benzersiz bir başvuru var. Belki de tüm programınızı başka bir yerde başvurulabilir. I. e. this == &temporarymümkün değildir.

Şimdi Eğer müvekkiliniz sana yalan söyledi ve sen olmadığında geçici alacağını vaat etti, daha sonra müşterinin bakım gerekmez emin olmak sorumluluğundadır. Eğer gerçekten dikkatli olmak istiyorsanız, bu iyi bir uygulama olacağına inanıyorum:

Class&
Class::operator=(Class&& other)
{
    assert(this != &other);
    // ...
    return *this;
}

I. e. Eğerkendine bir referans kabul edilen bu düzeltilmesi gereken istemci tarafında bir hata değildir.

Bütünlüğü için, burada dumb_array atama operatörü: bir hareket

dumb_array& operator=(dumb_array&& other)
{
    assert(this != &other);
    delete [] mArray;
    mSize = other.mSize;
    mArray = other.mArray;
    other.mSize = 0;
    other.mArray = nullptr;
    return *this;
}

Hareket normal kullanımı halinde ataması, 37* *delete [] mArray; bir no-op olmalı taşındı-nesne ve olacaktır. Uygulamaları bir nullptr mümkün olduğunca hızlı silme yapmak önemlidir.

Uyarı:

Bazı swap(x, x) iyi bir fikir, ya da sadece gerekli bir kötülük olduğunu iddia edecektir. Ve bu, eğer takas varsayılan takas giderse,-hareket kendini bir atama neden olabilir.

swap(x, x) olduğunu katılmıyorumhiçiyi bir fikir. Eğer kendi kodumu tespit edilmesi durumunda, bu performans bir hata düşünün ve bunu çözecektir. Ama buna izin vermek istiyorsanız, swap(x, x) tek-hareket-assignemnet kendi kendine yaptığı taşındı değer bir fark. dumb_array bizim örneğimizde bu, eğer biz sadece atlarsanız iddia ya taşındı-durum: plandan ayırmak tamamen zararsız olacaktır

dumb_array& operator=(dumb_array&& other)
{
    assert(this != &other || mSize == 0);
    delete [] mArray;
    mSize = other.mSize;
    mArray = other.mArray;
    other.mSize = 0;
    other.mArray = nullptr;
    return *this;
}

Kendini ata iki taşındı-(boş) dumb_array'ın, bir şey yanlış bir kenara programına yararsız talimatları ekleme yapamazsın. Bu aynı gözlem nesneleri büyük çoğunluğu için yapılabilir.

<Güncelleme>

Bu sorunu biraz daha düşündüm ve benim konum biraz değişti. Ben şimdi atama öz atama hoşgörülü olması gerektiğini, ama kopya atama ve hareket görevde post koşulları farklı olduğuna inanıyoruz:

Kopya ödev:

x = y;

y değeri değiştirilmemelidir post-koşul olmalıdır. &x == &y sonra bu postcondition çeviren: kendine kopyasını atama x değeri üzerinde hiçbir etkisi olmamalıdır.

Atama taşı:

x = std::move(y);

bir y geçerli ama belirsiz bir devlet olan post-koşul olmalıdır. &x == &y sonra bu postcondition çeviren: x geçerli ama belirsiz bir durum vardır. I. e. kendi kendine hareket atama yok-op olmak zorunda değil. Ama kaza değil. Bu durum post swap(x, x) sadece çalışmak için izin ile uyumludur:

template <class T>
void
swap(T& x, T& y)
{
    // assume &x == &y
    T tmp(std::move(x));
    // x and y now have a valid but unspecified state
    x = std::move(y);
    // x and y still have a valid but unspecified state
    y = std::move(tmp);
    // x and y have the value of tmp, which is the value they had on entry
}

Yukarıdaki eserler, sürece x = std::move(x) çökmez. Geçerli ama belirtilmeyen herhangi bir durumda x terk edebilirsiniz.

Üç yol dumb_array taşımak için atama operatör bunu başarmak için bu programı görüyorum

dumb_array& operator=(dumb_array&& other)
{
    delete [] mArray;
    // set *this to a valid state before continuing
    mSize = 0;
    mArray = nullptr;
    // *this is now in a valid state, continue with move assignment
    mSize = other.mSize;
    mArray = other.mArray;
    other.mSize = 0;
    other.mArray = nullptr;
    return *this;
}

Yukarıdaki uygulamaya göz yumuyor öz atama, ama *this other sonunuzun sıfır boyutlu dizi sonra kendi kendine hareket ödev, ne olursa olsun özgün değeri *this. Bu gayet iyi.

dumb_array& operator=(dumb_array&& other)
{
    if (this != &other)
    {
        delete [] mArray;
        mSize = other.mSize;
        mArray = other.mArray;
        other.mSize = 0;
        other.mArray = nullptr;
    }
    return *this;
}

Yukarıdaki uygulama kendi kendine atama kopya atama operatör aynı şekilde, hayır-op yaparak göz yumuyor. Bu da gayet iyi.

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

Yukarıda ise dumb_array tahrip olması gerektiğini kaynaklarını elinde yoksa sadece tamam mı"". hemen Eğer tek kaynak hafıza ise örneğin, yukarıda gayet iyi. dumb_array muhtemelen tutun dışlama kilitler veya açık devlet dosyaları, istemci olabilir makul beklediğiniz kaynaklarını belirtin hareket atama derhal serbest ve bu nedenle bu uygulama sorunlu olabilir.

İlk maliyeti iki ekstra depolar. İkinci maliyeti bir test-ve-dalıdır. Her iki çalışma. Hem C 11 standart Tablo 22 MoveAssignable gereksinimlerini karşılamak. Üçüncüsü de bunların uygulama ile ilişkisi olmayan bellek-kaynak-endişe çalışır.

Bir şube Nasıl? üç uygulamaları farklı maliyetler donanım bağlı olabilir: Kayıtları var ya da çok az?

-Uzaklaştırmak-hamle-öz atama, kopya öz atama aksine, mevcut değerini korumak için kullanılır.

</Güncelleme>

Son (umarım) edit Luc esinlenerek Danton'un yorum:

Eğer doğrudan bellek (ama bunu üsleri veya üye olabilir) yönetmek değil, yüksek düzeyde bir sınıf yazıyoruz, sonra taşı görevi en iyi uygulama sık sık

Class& operator=(Class&&) = default;

Bu her bir temel atar ve sırayla her üye hareket edecek, ve this != &other Çek içermez. Bu, çok yüksek performans ve temel özel güvenlik hiç değişmezler üsleri ve üyeleri arasında muhafaza edilmesi gerekir varsayarak verecektir. Müşterilerinize güçlü özel güvenlik gerektiren, strong_assign doğru onları işaret.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Doc Adams

    Doc Adams

    20 HAZİRAN 2007
  • The Bad Tutorials

    The Bad Tuto

    6 EKİM 2009
  • WOSU Public Media

    WOSU Public

    23 AĞUSTOS 2007