7 Soru: Operatör aşırı yüklemesi için temel kurallar ve deyimler nelerdir?

tarafından oluşturulan soru Wed, Jun 28, 2017 12:00 AM

Not: Cevaplar 'de belirli bir düzende verilmiştir, ancak birçok kullanıcı yanıtları verilen zaman yerine oylara göre sıraladığından, burada bir dizini en anlamlı oldukları sıraya göre yanıtlar :

(Not: Bu, Yığın Taşması C ++ SSS bölümüne giriş yapmak içindir Bu formda bir SSS sağlama fikrini eleştirmek istiyorsanız, tüm bunları başlatan metada yayınlama etiketi, bunun yapılacağı yer olur. Bu sorunun yanıtları C ++ sohbet odası , SSS fikri ilk etapta başladı, bu nedenle cevabınız bu fikri ortaya çıkaranlar tarafından okunması çok muhtemel.)

    
2010
  1. C ++ - FAQ etiketine devam edeceksek, bu şekilde girdilerin biçimlendirilmesi gerekir.
    2010-12-14 19: 45: 49Z
  2. Alman C ++ topluluğu için operatör aşırı yüklemesi hakkında kısa bir dizi makale yazdım: Bölüm 1: C ++ 'da aşırı yüklenme operatör , tüm operatörler için anlambilim, tipik kullanım ve özellikleri kapsar. Burada cevaplarınızla bazı örtüşmeler var, yine de bazı ek bilgiler var. Bölüm 2 ve 3, Boost.Operators'ı kullanmak için bir öğretici yapar. Onları çevirmemi ve cevapları olarak eklememi ister misiniz?
    2013-04-09 14: 11: 30Z
  3. Oh, ayrıca İngilizce çeviri de var: temel bilgiler ve ortak uygulama
    2017-07-31 14: 13: 18Z
7 Yanıtlar                              7                         

Genel operatörler aşırı yüklenme

Aşırı yükleme operatörlerinde yapılan işlerin çoğu kazan plakası kodudur. Operatörler sadece sözdizimsel şeker oldukları için, asıl işi düz işlevlerle yapılabilir (ve genellikle iletilir). Ancak bu kazan plakası kodunu doğru almanız önemlidir. Başarısız olursanız, operatörünüzün kodu derlenmez veya kullanıcılarınızın kodu derlenmez veya kullanıcılarınızın kodu şaşırtıcı şekilde davranır.

Atama Operatörü

Ödev hakkında söylenecek çok şey var. Bununla birlikte, çoğu zaten GMan’ın ünlü Kopyala ve Takas Et SSS’inde söylendi. , bu yüzden çoğunu burada atlayacağım, yalnızca referans için mükemmel atama operatörünü listeleyeceğim:

 
X& X::operator=(X rhs)
{
  swap(rhs);
  return *this;
}

Bitshift Operatörleri (Stream I /O için kullanılır)

Bitshift operatörleri << ve >>, yine de C'den devraldıkları bit manipülasyon fonksiyonları için donanım ara yüzünde kullanılmasına rağmen, çoğu uygulamada aşırı yüklenen akış giriş ve çıkış operatörleri olarak daha yaygın hale gelmiştir. Bit manipülasyon operatörleri olarak kılavuz aşırı yükleme için, aşağıdaki İkili Aritmetik Operatörler bölümüne bakınız. Kendi özel biçiminizi uygulamak ve nesneniz iostreams ile kullanıldığında ayrıştırma mantığını kullanmak için devam edin.

Akış işleçleri, en sık aşırı yüklenmiş işleçler arasında, sözdiziminin üye olup olmadıklarına ilişkin herhangi bir kısıtlama bulunmadığını belirttikleri ikili girişli işleçlerdir. Sol argümanlarını değiştirdiklerinden (akıntının durumunu değiştirdikleri için), kurallara göre sol operandının türünün üyeleri olarak uygulanmaları gerekir. Ancak, sol işlenenleri standart kitaplıktan akışlardır ve standart kitaplık tarafından tanımlanan akış çıkış ve giriş operatörlerinin çoğu gerçekten kendi türleriniz için çıkış ve giriş işlemlerini uyguladığınızda akış sınıflarının üyeleri olarak tanımlanır. standart kütüphanenin yayın türlerini değiştiremez. Bu yüzden bu operatörleri üye olmayan fonksiyonlar olarak kendi tiplerinize uygulamanız gerekiyor. Bu ikisinin kanonik formları şunlardır:

 
std::ostream& operator<<(std::ostream& os, const T& obj)
{
  // write obj to stream

  return os;
}

std::istream& operator>>(std::istream& is, T& obj)
{
  // read obj from stream

  if( /* no valid object of T found in stream */ )
    is.setstate(std::ios::failbit);

  return is;
}

operator>>'u uygularken, akımın durumunu manuel olarak ayarlamak, yalnızca okumanın başarılı olması durumunda gereklidir, ancak sonuç bekleneceği gibi değildir.

İşlev çağrısı operatörü

İşlevler oluşturmak için işlevler olarak da bilinen işlev çağrısı işleci, member işlevi olarak tanımlanmalıdır, bu nedenle her zaman için üye işlevleri Bunun dışında sıfır dahil herhangi bir sayıda ek argüman almak aşırı yüklenebilir.

İşte sözdiziminin bir örneği:

 this

Kullanım:

 
class foo {
public:
    // Overloaded call operator
    int operator()(const std::string& y) {
        // ...
    }
};

C ++ standart kütüphanesinde, işlev nesneleri her zaman kopyalanır. Bu nedenle kendi işlev nesneleriniz kopyalamak için ucuz olmalıdır. Bir işlev nesnesinin, kopyalanması pahalı olan verileri kullanması gerekiyorsa, bu verileri başka bir yerde depolamak ve işlev nesnesine başvurmak daha iyidir.

Karşılaştırma operatörleri

İkili infix karşılaştırma işleçleri, kurallara göre, üye olmayan işlevler olarak 1 uygulanmalıdır. Birlik ön ek ihlali

foo f;
int a = f("hello");
(aynı kurallara göre) bir üye işlevi olarak uygulanmalıdır. (ancak bunu aşırı yüklemek genellikle iyi bir fikir değildir.)

Standart kütüphanenin algoritmaları (ör. !) ve türleri (ör. std::sort()) her zaman yalnızca std::map’un bulunmasını bekler. Bununla birlikte, türünüzün kullanıcıları diğer tüm operatörlerin de bulunmasını bekler de, operator<'u tanımlarsanız, operatörün aşırı yüklenmesinin üçüncü temel kuralına uyduğunuzdan ve diğer tüm booleanı tanımladığınızdan emin olun. karşılaştırma operatörleri. Bunları uygulamanın kurallı yolu şudur:

 operator<

Burada dikkat edilmesi gereken en önemli şey, bu operatörlerin yalnızca ikisinin aslında bir şey yaptığı, diğerleri ise asıl işi yapmak için argümanlarını bu ikisinden birine yönlendiriyor olmasıdır.

Kalan ikili boole operatörlerini (

inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return  operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}
, ||) aşırı yükleme sözdizimi, karşılaştırma operatörlerinin kurallarına uyar. Ancak, bu 2 için makul bir kullanım örneği bulmanız pek mümkün değildir çok .

1 Tüm kurallarda olduğu gibi, bazen bunu da kırmak için sebepler olabilir. Eğer öyleyse, üye fonksiyonlar için && olacak olan ikili karşılaştırma operatörlerinin sol işleneninin de *this olması gerektiğini unutmayın. Dolayısıyla, üye işlevi olarak uygulanan bir karşılaştırma operatörünün bu imzayı taşıması gerekir:

 const

(En son

bool operator<(const X& rhs) const { /* do actual comparison with *this */ }
’a dikkat edin.)

2 Dahili const ve || sürümünün kısayol anlambilimi kullandığı belirtilmelidir. Kullanıcı tanımlı olanlar ise (çünkü yöntem çağrıları için sözdizimsel şeker oldukları için) kısayol anlambilimi kullanmazlar. Kullanıcı bu operatörlerden kısayol anlambilimine sahip olmasını bekler ve kodları buna bağlı olabilir, Bu nedenle onları tanımlamak ASLA tavsiye edilmez.

Aritmetik İşleçler

Unary aritmetik operatörleri

Unary artım ve azalış operatörleri hem prefix hem postfix lezzetlerinde gelir. Birinden diğerine söylemek gerekirse, postfix varyantları ek bir kukla int argümanı alır. Artış veya azalmayı aşırı yüklerseniz, her zaman hem önek hem de sonek sürümlerini uyguladığınızdan emin olun. İşte artımın kurallı uygulamasıdır, azalış aynı kuralları izler:

 &&

Postfix varyantının önek olarak uygulandığına dikkat edin. Ayrıca, postfix’in fazladan bir kopya yaptığını unutmayın. 2

Unary eksi ve aşırı yüklemeartı çok yaygın değildir ve muhtemelen en iyi şekilde kaçınılmalıdır. Gerekirse, muhtemelen üye işlevleri olarak aşırı yüklenmeleri gerekir.

2 Ayrıca, postfix varyantının daha fazla çalıştığını ve bu nedenle, prefix varyantından daha az verimli olduğunu unutmayın. Bu genellikle ön ek artıştan sonra önek artışını tercih etmek için iyi bir nedendir. Derleyiciler, yerleşik türler için ek postfix artış çalışmalarını genellikle en iyi duruma getirebilse de, kullanıcı tanımlı türler için aynı şeyi yapamayabilirler (bu, bir liste yineleyici gibi görünen bir şey olabilir).

class X {
  X& operator++()
  {
    // do actual increment
    return *this;
  }
  X operator++(int)
  {
    X tmp(*this);
    operator++();
    return tmp;
  }
};
'u kullanmaya alıştıktan sonra, i++ yerleşik bir tür değilse, ++i'u hatırlamak çok zorlaşır (artı bir türü değiştirirken kodu değiştirmeniz gerekir); Postfix açıkça gerekli olmadıkça, daima prefix artışını kullanma alışkanlığı.

İkili aritmetik işleçler

İkili aritmetik operatörler için, üçüncü temel kural operatörü aşırı yüklemesine uymayı unutmayın: i sağlarsanız, ayrıca + sağlarsanız, += sağlarsanız, -'u ihmal etmeyin, vb. Andrew Koenig’in ilk önce, bileşik atama operatörlerinin, bileşik olmayan meslektaşları için bir baz olarak kullanılabileceğini gözlemlemek için. Yani, -= operatörü +, += - vb. Uygulanır.

Temel kurallarımıza göre, -= ve arkadaşları üye olmamalıdır, sol argümanlarını değiştiren bileşik atama meslektaşları (+ vs.) üye olmalıdır. += ve += için örnek kod; diğer ikili aritmetik operatörleri de aynı şekilde uygulanmalıdır:

 +

class X {
  X& operator+=(const X& rhs)
  {
    // actual addition of rhs to *this
    return *this;
  }
};
inline X operator+(X lhs, const X& rhs)
{
  lhs += rhs;
  return lhs;
}
referans başına sonucunu verirken, operator+= sonucunun bir kopyasını döndürür. Tabii ki, bir referansın geri gönderilmesi genellikle bir kopyayı geri göndermekten daha etkilidir, ancak operator+ durumunda, kopyalamanın etrafında bir yol yoktur. operator+ yazdığınızda, sonucun yeni bir değer olmasını beklersiniz, bu nedenle a + b'un yeni bir değer döndürmesi gerekir. 3 Ayrıca, operator+’un sol işlemcisini const referansı yerine kopya ile kabul ettiğini unutmayın. Bunun nedeni, operator+'a kopya başına argümanını vermesi için verilen sebeple aynıdır.

Bit manipülasyon operatörleri operator= ~ & | ^ <<, aritmetik operatörler ile aynı şekilde uygulanmalıdır. Ancak, (çıkış ve giriş için >> ve << aşırı yükleme hariç) bunların aşırı yüklenmesi için çok az makul kullanım durumu vardır.

3 Yine, bundan alınacak ders >>'un genel olarak a += b'dan daha verimli olduğu ve mümkünse tercih edilmesi gerektiğidir.

Dizi Aboneliği

Dizi alt operatörü, bir sınıf üyesi olarak uygulanması gereken ikili bir operatördür. Veri öğelerine bir anahtarla erişime izin veren konteyner benzeri türler için kullanılır. Bunları sağlamanın kurallı şekli şudur:

 a + b

Sınıfınızdaki kullanıcıların

class X {
        value_type& operator[](index_type idx);
  const value_type& operator[](index_type idx) const;
  // ...
};
tarafından döndürülen veri öğelerini değiştirmesini istemiyorsanız (bu durumda const olmayan varyantı çıkaramazsınız), her zaman operatörün her iki türevini de sağlamanız gerekir.

value_type'nin yerleşik bir türe başvurduğu biliniyorsa, operatörün const varyantı, const referansı yerine bir kopyasına daha iyi döndürmelidir:

 operator[]

İşaretçi Benzeri Türler için İşleçler

Kendi yineleyicilerinizi veya akıllı işaretçilerinizi tanımlamak için, unary prefix dereference operatörü

class X {
  value_type& operator[](index_type idx);
  value_type  operator[](index_type idx) const;
  // ...
};
'u ve ikili infix pointer üye erişim operatörünü * aşırı yüklemelisiniz:  ->

Bunların da hemen hemen her zaman hem const hem de const olmayan bir sürüme ihtiyaç duyacağına dikkat edin.

class my_ptr {
        value_type& operator*();
  const value_type& operator*() const;
        value_type* operator->();
  const value_type* operator->() const;
};
operatörü için ->, value_type (veya class veya struct) türündeyse, union, sınıf dışı bir tür değeri döndürene kadar art arda operator->() olarak adlandırılır.

Tekli operatör adresi asla aşırı yüklenmemelidir.

operator->() için bu soruyu inceleyin. Nadiren kullanılır ve bu nedenle nadiren aşırı yüklenir. Aslında, yineleyiciler bile aşırı yüklemez.


Dönüşüm Operatörleri

ile devam edin     
977
2019-06-06 09: 33: 40Z
  1. operator->*() aslında son derece garip. Bir operator->() döndürmek gerekli değildir - aslında, sınıf türünün value_type* olması şartıyla olan başka bir sınıf türünü geri gönderebilir ve daha sonra aranır. Bu özyinelemeli operator->() s çağrısı, operator->() dönüş türü gerçekleşene kadar devam eder. Madness! :)
    2011-02-22 11: 20: 04Z
  2. İşaretçi benzeri operatörlerin const /const olmayan sürümlerine katılmıyorum, ör. `const value_type & operatörü * () const; `- bu, 49210’un yeniden düzenleme sırasında value_type*’u döndürmesi gibi olur; Başka bir deyişle: Bir const pointer bir const pointee anlamına gelmez. Aslında, T* const'u taklit etmek önemsiz değildir - bu standart kütüphanedeki const T&'un tamamının sebebidir. Sonuç: İmza T const * olmalıdır
    2013-04-09 14: 02: 52Z
  3. Bir yorum: Önerilen ikili aritmetik işleçlerinin uygulanması, olabileceği kadar verimli değil. Se Boost operatörleri başlıkları simetri notu: boost.org/doc /libs/1_54_0/libs/utility/operators.htm#symmetry İlk parametrenin yerel bir kopyasını kullanırsanız, + = yapın ve yerel kopyayı geri döndürürseniz bir kopya daha önlenebilir. Bu, NRVO optimizasyonunu mümkün kılar.
    2013-07-19 08: 36: 03Z
  4. Sohbette de belirttiğim gibi, const_iterator, reference_type operator*() const; pointer_type operator->() const yerine L <= R olarak da ifade edilebilir. İyileştirilmesi zor ifadelerde fazladan bir satır içi katmanı kurtarabilir (ayrıca Boost.Operators bunu nasıl uygular).
    2013-10-10 18: 24: 42Z
  5. @ UKMonkey: Neden sağladığım bağlantıyı izlemiyorsunuz? Orada, GMan, neden bu varsayımla sanatın yaklaşık 10-15 yıl geride olduğunu ağrılı bir şekilde açıkladı.
    2016-10-06 08: 05: 38Z

C ++ ile Operatör Aşırı Yüklemesinin Üç Temel Kuralı

C ++ 'da operatörün aşırı yüklenmesi söz konusu olduğunda, izlemeniz gereken üç temel kural vardır . Tüm bu kurallarda olduğu gibi, gerçekten de istisnalar var. Bazen insanlar onlardan saptı ve sonuç kötü kod değildi, ancak bu tür olumlu sapmalar çok azdı. En azından, gördüğüm bu 100 sapmadan 99'u haksızdı. Bununla birlikte, 1000 üzerinden 999 olabilir. Dolayısıyla, aşağıdaki kurallara uymanız daha iyi.

  1. Bir operatörün anlamı açık bir şekilde net ve tartışmasız olunca aşırı yüklenmemelidir. Bunun yerine bir kuyuya sahip bir işlev sağlayın seçim adı.
    Temel olarak, operatörlerin aşırı yüklenmesi için ilk ve en önemli kural, özünde şöyle diyor: Yapma . Bu garip görünebilir, çünkü operatörün aşırı yüklenmesi hakkında bilinmesi gereken çok şey var ve bu yüzden birçok makale, kitap bölümü ve diğer metinler bunlarla ilgileniyor. Ancak bu açıkça bariz kanıtlara rağmen, operatörün aşırı yüklenmesinin uygun olduğu şaşırtıcı derecede az sayıda vaka vardır . Bunun nedeni, operatörün uygulama alanındaki kullanımı iyi bilinmediği ve tartışmasız olmadığı sürece, bir operatör uygulamasının arkasındaki anlambilimin anlaşılmasının zor olmasıdır. Popüler inanışın aksine, bu hiç de böyle değil.

  2. Her zaman operatörün bilinen semantiğine bağlı kalın.
    C ++, aşırı yüklenmiş operatörlerin anlamlarıyla ilgili herhangi bir sınırlama getirmemektedir. Derleyiciniz, mutlu !(R < L) işlecini sağ işlemcisinden çıkartmak için uygulayan kodu memnuniyetle kabul edecektir. Bununla birlikte, böyle bir operatörün kullanıcıları, !(L > R)'dan +'u çıkarmak için a + b ifadesinden asla şüphelenmezler. Tabii ki, bu uygulama alanındaki operatörün anlamının tartışılmaz olduğunu varsayar.

  3. Daima bir dizi ilişkili işlemden fazlasını yapın.
    Operatörler birbirleriyle ilişkilidir ve diğerleriyle operasyonlar. Tipiniz a'u destekliyorsa, kullanıcılar da b'u arayabilir. a + b öneki artışını destekliyorsa, a += b'un da çalışmasını bekler. ++a olup olmadığını kontrol edebilirlerse, kesinlikle a++'u kontrol etmeyi de bekleyeceklerdir. Türünüzü kopyalayabiliyorlarsa, çalışmanın da çalışmasını beklerler.


Üye ile Karar KararıÜye olmayanlar .

    
469
2017-05-23 12: 34: 53Z
  1. Bunlardan herhangi birini ihlal ettiğini bildiğim tek şey a < b lol.
    2010-12-12 17: 50: 52Z
  2. @ Billy: Bazılarına göre, a > b dizesini bitiştirmeyi kötüye kullanmak bir ihlaldir, ancak artık çok iyi anlaşılmış bir praksis haline gelmiştir, bu nedenle doğal görünmektedir. Her ne kadar bir ev yapımı dize sınıfını hatırlamama rağmen, bu amaçla ikili boost::spirit kullanan 90'lı yıllarda gördüm (yerleşik praksis için BASIC'e atıfta bulundum). Fakat, evet, onu std lib'ine koymak temelde bunu taşa yerleştirdi. Aynı şey, IO, BTW için + ve &'yu kötüye kullanmak için de geçerlidir. Neden sola kaydırma açık bir çıktı işlemi olsun ki? Çünkü ilk "Merhaba, dünya!" Derken hepimiz bunu öğrendik. uygulama. Ve başka bir sebep yok.
    2010-12-12 19: 56: 25Z
  3. @ curiousguy: Eğer açıklamak zorundaysanız, açıkça görülmemiş ve tartışılmaz. Aynı şekilde aşırı yüklemeyi tartışmanız veya savunmanız gerekiyorsa.
    2011-12-02 12: 09: 36Z
  4. @ sbi: "akran incelemesi" her zaman iyi bir fikirdir. Bana göre kötü seçilmiş bir operatör kötü seçilmiş bir işlev adından farklı değil (çok gördüm). Operatör sadece fonksiyonlardır. Ne fazla ne az. Kurallar aynı. Ve bir fikrin iyi olup olmadığını anlamak için en iyi yol, ne kadar zaman anlaşıldığını anlamaktır. (Bu nedenle, akran incelemesi bir zorunluluktur, ancak akranlar dogma ve önyargıdan uzak kişiler arasında seçilmelidir.)
    2012-04-09 16: 57: 00Z
  5. @ sbi Bana göre, << ile ilgili tek kesin ve tartışmasız gerçek, bunun bir denklik ilişkisi olması gerektiğidir (IOW, NaN olmayan sinyal kullanmamanız gerekir). Konteynırlar üzerinde birçok faydalı denklik ilişkisi vardır. Eşitlik ne demektir? ">>, operator=='a eşittir", a ve b'un aynı matematiksel değere sahip olduğu anlamına gelir. (NaN olmayan) a'un matematiksel değeri kavramı açıktır, ancak bir kabın matematiksel değeri birçok farklı (tip özyinelemeli) faydalı tanımlara sahip olabilir. Eşitliğin en güçlü tanımı "aynı nesnelerdir" ve faydasızdır.
    2012-04-10 00: 49: 11Z

C ++ 'da operatörün aşırı yüklenmesinin genel sözdizimi

C ++ 'da yerleşik türler için operatörlerin anlamını değiştiremezsiniz, operatörler yalnızca 1 kullanıcı tanımlı türler için aşırı yüklenebilir. Yani, işlenenlerden en az birinin kullanıcı tanımlı türde olması gerekir. Diğer aşırı yüklenmiş fonksiyonlarda olduğu gibi, operatörler belirli bir parametre seti için yalnızca bir kez aşırı yüklenebilir.

Tüm operatörler C ++ 'ta aşırı yüklenemez. Aşırı yüklenemeyen operatörler arasında: b float . :: sizeof ve C ++, typeid'daki tek üçlü operatör

C ++ 'da aşırı yüklenebilen operatörler arasında şunlar vardır:

  • aritmetik operatörler: .* ?: + - * ve / % += -= *= (tüm ikili ekler); /= %= (unary öneki); + - (unary önek ve postfix)
  • bit manipülasyon: ++ -- & | ^ ve << >> &= |= ^= (tüm ikili ekler); <<= (unary öneki)
  • boolean cebiri: >>= ~ == != < > <= >= (tüm ikili ekler); || (unary öneki)
  • bellek yönetimi: && ! new new[]
  • örtük dönüşüm operatörleri
  • çeşitli: delete delete[] = [] -> (tüm ikili ekler); ->* , (tüm unary öneki) * (işlev çağrısı, n-ary eki)

Bununla birlikte, 'in bunların hepsini aşırı yükleyebildiğiniz , yapmanız gerektiği anlamına gelmez. Operatör aşırı yüklenmesinin temel kurallarına bakın.

C ++ 'da, operatörler özel adlara sahip işlevler biçiminde aşırı yüklenir. Diğer fonksiyonlarda olduğu gibi, aşırı yüklenmiş operatörler genellikle kendi kullanıcılarının üye fonksiyonu olarak uygulanabilir.ft operand'ın türü veya üye olmayan işlevler olarak. Her ikisini de seçmekte veya kullanmakta serbestseniz, çeşitli kriterlere bağlıdır. 2 x nesnesine uygulanan, & 3 numaralı bir operatör, () veya @ olarak. operator@(x) ve x.operator@() nolu nesnelere uygulanan bir ikili infix operatörü @, x veya y olarak adlandırılmaktadır. 4

Üye olmayan işlevler olarak uygulanan işleçler bazen işleçlerinin türünün arkadaşlarıdır.

1 “Kullanıcı tanımlı” terimi biraz yanıltıcı olabilir. C ++ yerleşik türler ve kullanıcı tanımlı türler arasında ayrım yapar. Birincisine, örneğin int, char ve double; ikincisi, standart kütüphaneden olanlar da dahil olmak üzere, kullanıcılar tarafından tanımlanmayan tüm yapı, sınıf, birlik ve enum türlerine aittir.

2 Bu, daha sonraki bir bölümde kaplıdır > bu SSS’nin.

3operator@(x,y), C ++ uygulamasında geçerli bir operatör değildir, bu yüzden onu yer tutucu olarak kullanıyorum.

4 C ++ 'daki tek üçlü operatör aşırı yüklenemez ve her zaman yalnızca n-ary operatörü üye işlevi olarak uygulanmalıdır.


C ++ 'da Operatör Aşırı Yüklemesinin Üç Temel Kuralı ' ya devam edin.

    
248
2017-05-23 12: 18: 28Z
  1. x.operator@(y) bir "bit manipülasyon" operatörü değil
    2011-12-01 12: 26: 48Z
  2. @ ikili ön ek değil, unary önek.
    2012-11-02 05: 15: 27Z
  3. %=, yüklenemez operatörler listesinden yoksun.
    2015-07-04 05: 35: 17Z
  4. @ Mateen Bunun özel bir operatörle ilgili olmadığını açıkça belirtmek için gerçek bir operatör yerine bir yer tutucu kullanmak istedim , ama hepsi için de geçerlidir. Ve bir C ++ programcısı olmak istiyorsanız, küçük izlemeye bile dikkat etmeyi öğrenmelisiniz. ~
    2015-09-11 10: 57: 02Z
  5. @ H.R .: Bu kılavuzu okuduysanız, neyin yanlış olduğunu bilirsiniz. Genel olarak, soruyla ilgili ilk üç cevabı okumanızı öneririm. Bu, hayatınızın yarım saatinden fazla olmamalı ve size temel bir anlayış kazandırmalıdır. Operatöre özgü sözdizimi daha sonra bakabilirsiniz. Özel probleminiz .*'u üye fonksiyonu olarak aşırı yüklemeyi denemenizi önerir, ancak ona ücretsiz bir fonksiyonun imzasını verdi. buradaki bölümüne bakın.
    2018-07-13 23: 11: 02Z

Üye ve Üye Olmayanlar Arasındaki Karar

İkili operatörler :) (atama), operator+() (dizi aboneliği), = (üye erişimi) ve n-ary [] (işlev çağrısı) operatörü her zaman üye işlevleri olarak uygulanmalıdır , çünkü dilin sözdizimi bunları gerektirir.

Diğer operatörler, üye veya üye olmayan olarak uygulanabilir. Bununla birlikte, bazıları, genellikle üye olmayan işlevler olarak uygulanmalıdır, çünkü sol operandları sizin tarafınızdan değiştirilemez. Bunlardan en öne çıkanları, sol işlenenleri değiştiremediğiniz standart kütüphaneden akış sınıfları olan giriş ve çıkış işleçleri -> ve ()'dur.

Bunları bir üye işlevi veya üye olmayan bir işlev olarak uygulamayı seçmeniz gereken tüm operatörler için, karar vermek için aşağıdaki kurallara uyun /p>

  1. Birlikli operatör ise, bunu bir member işlevi olarak uygulayın.
  2. İkili bir operatör her iki işletmeciyi eşit olarak ele alırsa (bunları değiştirmez), bu operatörü üye olmayan işlevi.
  3. İkili bir operatör not her iki işleyicisine de davranırsa equ (genellikle solunu değiştirir) operand), operandın özel bölümlerine erişmek zorundaysa, sol operandının tipinde bir member işlevi yapmak faydalı olabilir.

Elbette, tüm kurallarda olduğu gibi, istisnalar da var. Bir türünüz varsa

 <<

ve bunun için artırma ve azaltma işleçlerini aşırı yüklemek istiyorsanız, bunu bir üye işlevi olarak yapamazsınız, çünkü C ++ 'da enum tipleri üye işlevlere sahip olamaz. Bu yüzden serbest bir fonksiyon olarak aşırı yüklemeniz gerekir. Ve bir sınıf şablonu içine yerleştirilmiş bir sınıf şablonu için >>, sınıf tanımında satır içi bir üye işlevi olarak yapıldığında yazılması ve okunması çok daha kolaydır. Ancak bunlar gerçekten nadir istisnalar.

(Ancak, bir istisna yaparsanız, ,

enum Month {Jan, Feb, ..., Nov, Dec}
-un işleyiş konusunu unutma, üye işlevler için örtük operator<() argümanı olur. Operatör olmayan üye işlevi en soldaki tartışmayı const referansı olarak alır, this'u const referansı yapmak için üye fonksiyonla aynı operatörün sonunda const olması gerekir.)


Aşırı yükleme yapan genel operatörler ile devam edin.

    
230
2017-05-23 11: 55: 13Z
  1. Herb Sutter'ın Etkili C ++ (ya da C ++ Kodlama Standartları mı?) adlı öğesinin, üye olmayan işlevlerini üye işlevlerine tercih etmesi gerektiğini; sınıf. IMHO, kapsülleme nedeni, kurallara uymamanızdan öncelikli olmakla birlikte, kurallar kuralınızın kalite değerini düşürmez.
    2010-12-12 13: 36: 08Z
  2. @ paercebal: Etkili C ++ , Meyers, C ++ Kodlama Standartları , Sutter'a aittir. Hangisini kastediyorsunuz? Her neyse, *this'un üye olmama fikrinden hoşlanmıyorum. Sol operandını değiştirmek zorunda kalıyor, bu yüzden tanımı gereği derinlerine inmek zorunda. Üye olamayarak ne elde edersiniz?
    2010-12-12 13: 39: 35Z
  3. @ sbi: C ++ Kodlama Standartlarında Madde 44 (Sutter) Üye olmayan işlevleri yazmayı tercih edin , elbette, yalnızca Bu fonksiyonu sadece sınıfın genel arayüzünü kullanarak yazınız. Eğer yapamazsanız (veya başaramazsanız ancak performansı kötü şekilde engeller), o zaman üye veya arkadaş olmanız gerekir.
    2010-12-12 15: 45: 00Z
  4. @ sbi: Hata, Etkili, İstisnai ... Adlarını karıştırmama şaşırmam. Yine de, bir nesneye ait özel /korumalı verilere erişimi olan işlevlerin sayısını mümkün olduğunca sınırlamak gerekir. Bu şekilde, sınıfınızın kapsüllemesini artırarak bakımını /testini /evrimini kolaylaştırırsınız.
    2010-12-12 16: 51: 07Z
  5. @ sbi: Bir örnek. Bir String sınıfını hem const hem de operator+=() yöntemleriyle kodladığınızı varsayalım. operator += yöntemi daha eksiksizdir, çünkü parametrenin bir alt dizisini i dizininden n -1: append'a ekleyebilirsiniz. append'un append(string, start, end) ve += ile eklenmesi mantıklı görünüyor. O anda, ekleme, bir üye yöntemi olabilir, ancak start = 0'un bir üye olması gerekmez ve üye olmamak, String'in içindekilerle çalınan kod miktarını azaltır, bu yüzden iyi bir şey ... . ^ _ ^ ...
    2010-12-12 16: 58: 09Z

Dönüşüm Operatörleri (Kullanıcı Tanımlı Dönüşümler olarak da bilinir)

C ++ 'da dönüşüm işleçleri, derleyicinin türlerinizle diğer tanımlanmış türler arasında dönüşüm yapmasına izin veren işleçler oluşturabilirsiniz. İki tür dönüştürme operatörü vardır: örtük ve açık olanlar.

Örtük Dönüşüm İşleçleri (C ++ 98 /C ++ 03 ve C ++ 11)

Örtük bir dönüşüm operatörü derleyicinin örtük olarak dönüştürmesini sağlar (dönüşüm bahsi gibi)ween end = string.size ve operator +=) kullanıcı tanımlı bir türün değerini başka bir türe yazın.

Aşağıdaki, örtük bir dönüştürme işlecine sahip basit bir sınıftır:

 int

Örtük dönüştürme operatörleri, tek değişkenli kurucular gibi, kullanıcı tanımlı dönüşümlerdir. Derleyiciler bir çağrıyı aşırı yüklenmiş bir işlevle eşleştirmeye çalışırken kullanıcı tarafından tanımlanmış bir dönüşüm sağlar.

 long

İlk önce bu çok yardımcı görünüyor, ancak bununla ilgili sorun, örtük dönüşümün beklendiği zaman bile başlamaması. Aşağıdaki kodda

class my_string {
public:
  operator const char*() const {return data_;} // This is the conversion operator
private:
  const char* data_;
};
,
void f(const char*);

my_string str;
f(str); // same as f( str.operator const char*() )
bir lvalue , böylece ilk eşleşmiyor:  void f(const char*)

Yeni başlayanlar bu hatayı kolayca alırlar ve hatta deneyimli C ++ programcıları bazen şaşırır, çünkü derleyici şüphelendikleri bir aşırı yük alır. Bu sorunlar açık dönüşüm operatörleri tarafından hafifletilebilir.

Açık Dönüşüm Operatörleri (C ++ 11)

Örtük dönüşüm işleçlerinden farklı olarak, açıkça dönüşüm işleçleri beklemeyince asla devreye girmez. Aşağıdakiler, açıkça bir dönüşüm operatörüne sahip basit bir sınıftır:

 my_string()

void f(my_string&);
void f(const char*);

f(my_string());
'a dikkat edin. Artık örtük dönüştürme işleçlerinden beklenmeyen kodu çalıştırmaya çalıştığınızda, bir derleyici hatası alıyorsunuz:  
class my_string {
public:
  explicit operator const char*() const {return data_;}
private:
  const char* data_;
};

Açık döküm operatörünü çağırmak için, explicit, bir C stili döküm veya bir yapıcı stili döküm kullanmanız gerekir (ör.

prog.cpp: In function ‘int main()’:
prog.cpp:15:18: error: no matching function for call to ‘f(my_string)’
prog.cpp:15:18: note: candidates are:
prog.cpp:11:10: note: void f(my_string&)
prog.cpp:11:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘my_string&’
prog.cpp:12:10: note: void f(const char*)
prog.cpp:12:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘const char*’
).

Bununla birlikte, bunun bir istisnası vardır: Derleyicinin örtük olarak static_cast'a dönüştürmesine izin verilir. Ek olarak, derleyicinin T(value)'a dönüştürüldükten sonra başka bir gizli dönüşüm yapmasına izin verilmez (bir derleyicinin bir seferde 2 örtülü dönüşüm yapmasına izin verilir, ancak en fazla yalnızca 1 kullanıcı tarafından tanımlanan dönüşüm).

Derleyici bool'u "geçmiş" bırakmayacağından, açık dönüşüm operatörleri şimdi

Aşırı Yükleme bool ve bool 'a devam edin.

    

155
2017-05-23 12: 26: 29Z

new ve delete'un Aşırı Yüklenmesi

Note: Bu, yalnızca new ve delete’un aşırı yüklenmesinin stax ile ilgilidir, bu tür aşırı yüklenmiş operatörlerin uygulama . Aşırı yükleme anlamının new ve delete’un kendi SSS’sini hakettiğini düşünüyorum. ” , operatör aşırı yüklemesi başlığı altında asla adaleti yerine getiremem.

Temel

C ++ 'da, bir yeni ifade yazdığınızda bu ifade değerlendirildiğinde new gibi iki şey olur: Önce delete ham bellek elde etmek için çağrılır ve ardından bu ham belleği geçerli bir nesneye dönüştürmek için uygun new T(arg) yapıcısı çağrılır. Aynı şekilde, bir nesneyi sildiğinizde, önce onun yıkıcısı denir, sonra hafıza operator new'a döndürülür.
C ++, bu işlemlerin ikisini de ayarlamanıza olanak tanır: hafıza yönetimi ve ayrılan hafızadaki nesnenin yapımı /imhası. İkincisi, bir sınıf için yapıcılar ve yıkıcılar yazarak yapılır. İnce ayarlı hafıza yönetimi kendi T ve operator delete'unuzu yazarak yapılır.

Operatör aşırı yüklenmesinin temel kurallarından ilki - yapmayın - özellikle operator new ve operator delete aşırı yüklemesi için geçerlidir. Bu operatörleri aşırı yüklemenin neredeyse tek nedeni performans sorunları ve bellek kısıtlamaları ve çoğu durumda diğer eylemlerdir. ( kullanılan algoritma değişiklikleri gibi), bellek yönetimini ince ayarlamaya çalışmaktan çok daha fazla daha yüksek maliyet /kazanç oranı sağlayacaktır.

C ++ standart kütüphanesi, önceden tanımlanmış bir dizi new ve delete operatörüyle birlikte gelir. En önemlileri şunlardır:

 new

İlk ikisi, bir nesne için hafızayı ayırır /çıkarır, ikincisi bir nesne dizisi için ikidir. Bunların kendi versiyonlarını sağlarsanız, aşırı yüklenmiyor, ancak standart kütüphanedekilerden fazlasını alıyor.
delete'u aşırı yüklerseniz, asla aramaya niyetli olmasanız bile, eşleşen

void* operator new(std::size_t) throw(std::bad_alloc); 
void  operator delete(void*) throw(); 
void* operator new[](std::size_t) throw(std::bad_alloc); 
void  operator delete[](void*) throw(); 
'u da aşırı yüklemelisiniz. Bunun nedeni, eğer bir yapıcı yeni bir ifadenin değerlendirilmesi sırasında atarsa, çalışma zamanı sisteminin belleği, içinde nesneyi oluşturmak için belleği tahsis etmek için çağrılan operator new ile eşleşen operator delete'a döndürmesidir. operator delete ile eşleşen, varsayılan olana denir ve bu neredeyse her zaman yanlıştır. operator new ve operator delete'u aşırı yüklerseniz, dizi değişkenlerini de aşırı yüklemeyi düşünmelisiniz.

Yerleşim new

C ++, yeni ve silme operatörlerinin ek argümanlar almalarına izin verir.
Sözde yerleşim yeni, belirli bir adreste iletilen bir nesne oluşturmanıza olanak sağlar:

 delete

Standart kitaplık, bunun için yeni ve silme operatörlerinin uygun aşırı yüklemeleriyle birlikte gelir:

 new

Yukarıda verilen yeni yerleşim için örnek kodda, X'in kurucusu istisna atamadıkça

class X { /* ... */ };
char buffer[ sizeof(X) ];
void f()
{ 
  X* p = new(buffer) X(/*...*/);
  // ... 
  p->~X(); // call destructor 
} 
'un asla çağrılmadığını unutmayın.

Ayrıca

void* operator new(std::size_t,void* p) throw(std::bad_alloc); 
void  operator delete(void* p,void*) throw(); 
void* operator new[](std::size_t,void* p) throw(std::bad_alloc); 
void  operator delete[](void* p,void*) throw(); 
ve operator delete’u başka argümanlarla aşırı yükleyebilirsiniz. Yeni yerleştirme ek argümanında olduğu gibi, bu argümanlar new anahtar sözcüğünden sonra parantez içinde de listelenmiştir. Sadece tarihsel nedenlerden ötürü, bu tür değişkenlere, argümanları belirli bir adrese yerleştirilmeseler bile, yeni yerleştirme denir.

Sınıfa özel yeni ve sil

Genellikle, bellek yönetimine ince ayar yapmak istersiniz, çünkü ölçüm, belirli bir sınıfın veya bir grup ilgili sınıfın örneklerinin sık sık oluşturulduğunu ve imha edildiğini ve çalışma zamanı sisteminin varsayılan bellek yönetiminin gösterildiğini göstermiştir. Genel performans için ayarlanmış, bu özel durumda verimsiz ilgilenir. Bunu geliştirmek için, yeni bir şeyi aşırı yükleyebilir ve belirli bir sınıf için silebilirsiniz:

 delete

Aşırı yüklenmiş, yeni ve sil statik eleman işlevleri gibi davranır. new objeleri için

class my_class { 
  public: 
    // ... 
    void* operator new();
    void  operator delete(void*,std::size_t);
    void* operator new[](size_t);
    void  operator delete[](void*,std::size_t);
    // ... 
}; 
argümanı her zaman my_class olacaktır. Bununla birlikte, bu operatörler aynı zamanda, dinamik olarak tahsis edilmiş türetilmiş sınıfların nesnelerine de çağrılır, bu durumda bundan daha büyük olabilir.

Global yeni ve sil

Dünyadaki yeniyi aşırı yüklemek ve silmek için, standart kütüphanenin önceden tanımlanmış operatörlerini kendimizinkiyle değiştirin. Ancak, bu nadiren yapılması gerekenler.

    
140
2017-10-23 00: 54: 25Z
  1. Global operatörün yeni ve silme işleminin değiştirilmesinin genellikle performans için olduğunu kabul etmiyorum: aksine, genellikle hata izleme için.
    2010-12-12 15: 14: 32Z
  2. Ayrıca, aşırı yüklenmiş yeni bir operatör kullanıyorsanız, eşleşen argümanlarla bir silme operatörü sağlamanız gerektiğini de unutmamalısınız. Bunu, global new /delete bölümünün çok ilgi çekici olmadığı bir yerde söylüyorsunuz.
    2010-12-12 15: 17: 11Z
  3. @ Yttrill işleri karıştırıyor. anlamı aşırı yüklenir. "Operatör aşırı yüklenmesi" ne demek, anlamın aşırı yüklenmesi anlamına gelir. Kelimenin tam anlamıyla işlevlerin aşırı yüklendiği anlamına gelmez ve özellikle operatör yeni, Standardın sürümünü aşırı yüklemeyecektir. @sbi tersini iddia etmez. "Aşırı yükleme işleci" demek yaygın olduğu için "yeni aşırı yükleme" olarak adlandırmak yaygındır.
    2010-12-13 04: 51: 28Z
  4. @ sbi: Bkz. (veya daha iyisi, link) gotw.ca/publications/mill15.htm . Sadece std::size_t'u yeni kullanan insanlara karşı sadece iyi bir uygulamadır.
    2011-07-27 08: 36: 29Z
  5. "Eşleşen bir operatör silme işlemi sağlamazsanız, varsayılan terim" - > Aslında, herhangi bir argüman eklerseniz ve eşleşen bir silme oluşturmazsanız, hiçbir operatör silme işlemi yapılmaz ve bellek sızıntısı olur. (15.2.2, nesnenin işgal ettiği depolama alanı, yalnızca uygun bir ... işleç silme bulunduğunda kaldırılır)
    2015-01-23 20: 30: 54Z

sizeof(my_class), nesnelerin nothrow'a veya bir dosyaya aktarılması için neden çalışmıyor üye işlevi?

Sahip olduğunuzu varsayalım:

 operator<<

Buna göre, kullanamazsınız:

 std::cout

struct Foo
{
   int a;
   double b;

   std::ostream& operator<<(std::ostream& out) const
   {
      return out << a << " " << b;
   }
};
,
Foo f = {10, 20.0};
std::cout << f;
'un üye işlevi olarak aşırı yüklendiğinden, operatörün LHS'si bir operator<< nesnesi olmalıdır. Bu, kullanmanız gerekeceği anlamına gelir:  Foo

çok sezgisel değil.

Bunu üye olmayan bir işlev olarak tanımlarsanız,

 Foo

Kullanabileceksiniz:

 
Foo f = {10, 20.0};
f << std::cout

ki bu çok sezgiseldir.

    
37
2017-05-23 12: 10: 45Z
struct Foo
{
   int a;
   double b;
};

std::ostream& operator<<(std::ostream& out, Foo const& f)
{
   return out << f.a << " " << f.b;
}
kaynak yerleştirildi İşte