Aşırı yüklenecek ortak operatörler
Aşırı yükleme operatörlerindeki çalışmaların çoğu kazan plakası kodudur. Operatörler sadece sözdizimsel şeker oldukları için, gerçek çalışmaları 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ü
Görevlendirme hakkında söylenecek çok şey var. Bununla birlikte, çoğu GMan'ın ünlü Kopyala-Takas SSS bölümünde zaten söylendi , bu yüzden çoğunu burada atlayacağım, sadece referans için mükemmel atama operatörünü listeleyeceğim:
X& X::operator=(X rhs)
{
swap(rhs);
return *this;
}
Bitshift Operatörleri (Akış G / Ç için kullanılır)
Bitshift operatörleri <<
ve C'den devraldıkları >>
bit manipülasyon fonksiyonları için hala donanım arayüzünde kullanılmasına rağmen, çoğu uygulamada aşırı yüklü akım giriş ve çıkış operatörleri olarak daha yaygın hale gelmiştir. Bit manipülasyon operatörleri olarak aşırı yükleme için, aşağıdaki İkili Aritmetik Operatörler bölümüne bakınız. Nesneniz iostreams ile kullanıldığında kendi özel biçiminizi uygulamak ve mantığı ayrıştırmak için devam edin.
Akış işleçleri, en çok aşırı yüklenen işleçler arasında, sözdiziminin üye olup olmamaları konusunda herhangi bir kısıtlama belirtmediği ikili infix işleçleridir. Sol argümanlarını değiştirdiklerinden (akışın durumunu değiştirdiler), başparmak kurallarına göre sol işlenen türünün üyeleri olarak uygulanmalıdırlar. Bununla birlikte, sol işlenenleri standart kitaplıktan gelen akışlardır ve standart kitaplık tarafından tanımlanan akış çıktısı ve girdi işleçlerinin çoğu gerçekten de kendi sınıflarınız için çıktı ve girdi işlemleri uyguladığınızda, akış sınıflarının üyeleri olarak tanımlanırken, standart kitaplığın akış türlerini değiştiremez. Bu nedenle bu operatörleri üye olmayan işlevler olarak kendi türleriniz için uygulamanız gerekir. İkisinin kanonik biçimleri ş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;
}
Uygulama yaparken operator>>
, akışın durumunu manuel olarak ayarlamak yalnızca okuma başarılı olduğunda gereklidir, ancak sonuç beklenen şey değildir.
İşlev çağrısı operatörü
İşlev nesneleri oluşturmak için kullanılan ve işlevler olarak da bilinen işlev çağrısı işlecinin üye işlevi olarak tanımlanması gerekir , bu nedenle her zaman this
üye işlevlerinin örtük argümanına sahiptir. Bunun dışında, sıfır da dahil olmak üzere herhangi bir sayıda ek argüman almak aşırı yüklenebilir.
İşte sözdiziminin bir örneği:
class foo {
public:
// Overloaded call operator
int operator()(const std::string& y) {
// ...
}
};
Kullanımı:
foo f;
int a = f("hello");
C ++ standart kitaplığı boyunca, işlev nesneleri her zaman kopyalanır. Bu nedenle kendi işlev nesnelerinizin kopyalanması ucuz olmalıdır. Bir işlev nesnesinin kesinlikle kopyalanması pahalı olan verileri kullanması gerekiyorsa, bu verileri başka bir yerde saklamak ve işlev nesnesinin başvurmasını sağlamak daha iyidir.
Karşılaştırma operatörleri
İkili infix karşılaştırma operatörleri, başparmak kurallarına göre üye olmayan fonksiyonlar 1 olarak uygulanmalıdır . Tekli önek olumsuzlaması !
(aynı kurallara göre) bir üye işlevi olarak uygulanmalıdır. (ancak aşırı yüklenmesi genellikle iyi bir fikir değildir.)
Standart kütüphanenin algoritmaları (örn. std::sort()
) Ve türleri (ör. std::map
) Her zaman sadece operator<
mevcut olmayı bekler . Bununla birlikte, türünüzün kullanıcıları diğer tüm operatörlerin de var olmasını bekler , bu nedenle, tanımlarsanız operator<
, operatör aşırı yüklemesinin üçüncü temel kuralına uyduğunuzdan ve diğer tüm boolean karşılaştırma operatörlerini tanımladığınızdan emin olun. Onları uygulamanın kanonik yolu şudur:
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);}
Burada dikkat edilmesi gereken önemli nokta, bu operatörlerden sadece ikisinin gerçekte bir şey yapmasıdır, diğerleri sadece gerçek işi yapmak için argümanlarını bu ikisinden birine iletmektedir.
Kalan ikili boole işleçlerinin ( ||
, &&
) aşırı yüklenmesi sözdizimi , karşılaştırma işleçlerinin kurallarına uyar. Ancak, öyle çok Bunlar için makul kullanım davayı bulacağını olası 2 .
1 Tüm temel kurallarda olduğu gibi, bazen bunu kırmak için de nedenler olabilir. Eğer öyleyse, üye fonksiyonları için ikili karşılaştırma operatörlerinin sol tarafındaki işlenenin de olması *this
gerektiğini unutmayın const
. Dolayısıyla, üye işlevi olarak uygulanan bir karşılaştırma operatörünün bu imzası olması gerekir:
bool operator<(const X& rhs) const { /* do actual comparison with *this */ }
(Sonundaki notu not edin const
.)
2 Yerleşik sürümünün kısayol anlambiliminin kullanıldığı ||
ve belirtildiği unutulmamalıdır &&
. Kullanıcı tanımlı olanlar (yöntem çağrıları için sözdizimsel şeker oldukları için) kısayol anlambilimi kullanmazlar. Kullanıcı bu operatörlerin kısayol anlambilimine sahip olmasını bekler ve kodları buna bağlı olabilir, Bu nedenle ASLA bunları tanımlamanız kesinlikle tavsiye edilmez.
Aritmetik operatörler
Tekli aritmetik işleçler
Tekli arttırma ve azaltma operatörleri hem önek hem de düzeltme sonrası lezzetinde gelir. Birinden diğerine söylemek için postfix varyantları ek bir kukla int argümanı alır. Artışı veya azalmayı aşırı yüklerseniz, her zaman hem önek hem de son düzeltme sürümlerini uyguladığınızdan emin olun. İşte artışın kanonik uygulaması, azaltma aynı kuralları izler:
class X {
X& operator++()
{
// do actual increment
return *this;
}
X operator++(int)
{
X tmp(*this);
operator++();
return tmp;
}
};
Postfix varyantının önek açısından uygulandığını unutmayın. Postfix'in fazladan bir kopya yaptığını da unutmayın. 2
Tekli eksi ve artı aşırı yükleme çok yaygın değildir ve muhtemelen en iyi şekilde önlenir. Gerekirse, üye işlevleri olarak aşırı yüklenmeleri gerekir.
2 Ayrıca postfix varyantının önek varyantından daha fazla çalıştığını ve bu nedenle kullanımı daha az verimli olduğunu unutmayın. Bu genellikle düzeltme sonrası artışa göre önek artışını tercih etmek için iyi bir nedendir. Derleyiciler genellikle yerleşik türler için ek düzeltme sonrası artış çalışmasını optimize edebilirken, kullanıcı tanımlı türler için aynı şeyi yapamayabilirler (bu, liste yineleyici gibi masum bir şekilde görünebilir). Yapmanız alıştım kez i++
, bunu yapmak için hatırlamak çok zor olur ++i
zaman yerine i
daha iyidir, böylece yerleşik bir türü (bir tür değiştirirken artı değişim koduna olurdu), daima alışkanlık haline ila değildir postfix açıkça gerekmedikçe önek artışı kullanılarak.
İkili aritmetik işleçler
İkili aritmetik operatörler, üçüncü temel kural operatör aşırı yüklenmesini itaat etmeyi unutmayın: Eğer sağlarsanız +
, aynı zamanda sağlamak +=
sağladığınız takdirde -
, değil omit yok -=
vs. Andrew Koenig bileşik atama olduğunu gözlemleyen ilk olduğu söylenir operatörler, bileşik olmayan muadilleri için bir baz olarak kullanılabilir. Yani, operatör olduğu +
anlamında uygulanan +=
, -
açısından uygulanmaktadır -=
vb
Temel kurallarımıza göre +
ve arkadaşları, üye olmamalı, bileşik atama muadilleri ( +=
vb.), Sol argümanlarını değiştirerek üye olmalıdır. Burada için örnek teşkil eden bir kod +=
ve +
; diğer ikili aritmetik işleçler 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;
}
operator+=
referans başına sonucunu döndürürken sonucunun operator+
bir kopyasını döndürür. Tabii ki, bir referansın döndürülmesi genellikle bir kopyayı döndürmekten daha etkilidir, ancak bu durumda operator+
kopyalamanın etrafında bir yol yoktur. Eğer yazarken a + b
, sonuç neden olan yeni bir değer, olmasını bekliyoruz operator+
yeni bir değer döndürmek zorundadır. 3
Ayrıca operator+
sol işlenenini const referansı yerine kopya ile alan not edin . Bunun nedeni, operator=
her kopya için argüman alma nedeniyle aynıdır .
Bit manipülasyon operatörleri ~
&
|
^
<<
>>
, aritmetik operatörlerle aynı şekilde uygulanmalıdır. Bununla birlikte, (aşırı yükleme <<
ve >>
çıkış ve giriş hariç), bunların aşırı yüklenmesi için çok az makul kullanım durumu vardır.
3 Yine, bundan alınacak ders a += b
, genel olarak, a + b
mümkünse daha verimli ve tercih edilmesi gerektiğidir.
Dizi Aboneliği
Dizi alt simge işleci, sınıf üyesi olarak uygulanması gereken bir ikili işleçtir. Veri öğelerine bir tuşla erişilmesini sağlayan kapsayıcı benzeri türler için kullanılır. Bunları sağlamanın kanonik şekli şudur:
class X {
value_type& operator[](index_type idx);
const value_type& operator[](index_type idx) const;
// ...
};
Sınıfınızdaki kullanıcıların döndürdüğü veri öğelerini değiştirmesini istemiyorsanız operator[]
(bu durumda sabit olmayan değişkeni atlayabilirsiniz), her zaman operatörün her iki değişkenini de sağlamalısınız.
Value_type öğesinin yerleşik bir türe başvurduğu biliniyorsa, operatörün const varyantı const referansı yerine bir kopyasını daha iyi döndürmelidir:
class X {
value_type& operator[](index_type idx);
value_type operator[](index_type idx) const;
// ...
};
İşaretçi Türleri için İşleçler
Kendi yineleyicilerinizi veya akıllı işaretçilerinizi tanımlamak için, tekli önek dereference operatörünü *
ve ikili infix pointer üye erişim operatörünü aşırı yüklemeniz gerekir ->
:
class my_ptr {
value_type& operator*();
const value_type& operator*() const;
value_type* operator->();
const value_type* operator->() const;
};
Bunların da neredeyse her zaman hem sabit hem de sabit olmayan bir sürüme ihtiyacı olacağını unutmayın. İçin ->
operatörün, eğer value_type
ait class
(ya da struct
ya da union
, başka bir) tip operator->()
bir kadar ardışık olarak adlandırılır operator->()
olmayan sınıf türü getirileri bir değer.
Operatörün normal adresi asla aşırı yüklenmemelidir.
İçin operator->*()
bkz bu soruyu . Nadiren kullanılır ve bu nedenle nadiren aşırı yüklenir. Aslında, yineleyiciler bile aşırı yüklenmez.
Dönüşüm Operatörlerine Devam Et