Türlerinizi Yerleşik Gibi Hissettirin
std::string türünün birleştirme için a + b ve yazdırma için cout << s yazmanıza izin verdiğini zaten biliyorsunuz. Bunlar özel derleyici hileleri değildir - tuhaf adlara sahip sıradan fonksiyonlardır. Operatör aşırı yükleme, sizin sınıflarınızın aynı söz dizimine bağlanmasını sağlayan özelliktir; böylece bir Vector2 veya Money türü tam olarak bir int gibi toplanabilir, karşılaştırılabilir ve yazdırılabilir.
Mekanizma bir kez gördüğünüzde basittir: a + b gibi bir ifade bir kısaltmadır. Derleyici bunu operator+ adlı bir fonksiyona yapılan çağrı olarak yeniden yazar ve işlenen türleriyle eşleşen birini arar. Bu fonksiyonu sınıfınız için tanımlayın ve a + b aniden çalışmaya başlar. Bu aslında özelleşmiş bir fonksiyon aşırı yükleme biçimidir - oradaki aynı ad çözümleme kuralları geçerlidir, sadece operatör biçimli adlarla.
Fonksiyonun her iki işleneni de const& ile aldığına dikkat edin: aritmetik, girdilerini değiştirmemelidir ve referanslar kopyalamayı önler. Yeni bir Vector2 değer olarak döndürür - p + q, tıpkı 2 + 3'ün 2'yi değiştirmemesi gibi, p veya q'ya dokunmadan taze bir sonuç üretmelidir.
Üye vs Üye Olmayan
Bir operatörü tanımlayabileceğiniz iki yer vardır: sınıfın bir üyesi olarak veya serbest (üye olmayan) fonksiyon olarak. Üye olarak, sol işlenen örtük this'tir, dolayısıyla ikili bir operatör yalnızca bir açık parametre alır:
Parametre listesinden sonraki const önemlidir: a + b, a'yı değiştirmemelidir, bu yüzden üye const olarak işaretlenir. Doğası gereği sol işlenene bağlı olan ve onun üzerinde dönüşüme ihtiyaç duymayan operatörler için üye biçimini kullanın - +=, [], (), -> ve -x veya ++x gibi tekli operatörler.
Üyelerdeki sorun: sol işlenen dönüştürülemez. Yukarıdaki üye operator+ ile a + 50 çalışır (50, sağ taraf için Money'ye dönüşür), ancak 50 + a derlenemez - sol işlenen 50 bir int'tir ve int'e bir üye fonksiyon ekleyemezsiniz. Üye olmayan bir operatör bunu düzeltir, çünkü işlenenlerin ikisi de açık parametredir ve ikisi de dönüştürülebilir:
Pratik kural: simetrik ikili operatörleri (+, ==, *) üye olmayan yapın ki dönüşümler her iki tarafta da çalışsın; sol işleneni değiştirmesi gereken veya ona bağlı olan operatörleri (+=, [], =) üye yapın.
Akış Operatörünü Aşırı Yükleme
Açık ara en sık aşırı yüklenen operatör, yazdırma için kullanılan <<'dir. Bunu sınıfınızın bir üyesi yapamazsınız, çünkü sol işlenen sizin türünüz değil bir std::ostream'dir (cout gibi) ve ostream'in sahibi siz değilsiniz. Bu yüzden o her zaman, akışı sabit olmayan referansla alıp geri döndüren üye olmayan bir fonksiyondur:
Bunu işe yarar kılan iki ayrıntı vardır. Akış referansla (ostream&) geçirilir ve döndürülür - akışlar kopyalanamaz ve aynı akışı döndürmek, cout << "p = " << p << "\n" ifadesini zincirlemenizi sağlayan şeydir. Her <<, bir sonraki <<'in bağlanacağı bir şeyi olsun diye akışı döndürür. return os; ifadesini unutursanız zincirleme bozulur.
Karşılaştırma Operatörleri
Nesnelerinizi ==, < ve benzerleriyle karşılaştırmak için karşılaştırma operatörlerini aşırı yükleyin. C++20'den önce her birini elle yazardınız; ana tuzak, operator<'in bir bool döndürmesi ve tutarlı bir sıralama tanımlaması gerektiğidir:
Altı karşılaştırmanın (==, !=, <, <=, >, >=) tümünü elle yazmak sıkıcı ve hataya açıktır. C++20, üç yönlü karşılaştırma operatörü <=>'i ("uzay gemisi") ekledi. Bunu == ile birlikte varsayılana ayarlamak, tüm karşılaştırmaları sizin için üretir:
= default, derleyiciye üyeleri bildirim sırasına göre karşılaştırmasını söyler ki bu da tam olarak elle yazacağınız sözlüksel sıralamadır. Modern derleyicilerde bunu tercih edin.
Atama Operatörü ve Tuzakları
operator= (kopya atama) özeldir: derleyici sizin için bir tane üretir ve basit sınıflar için bu varsayılan doğrudur. Yalnızca sınıfınız bir kaynağı - ham bellek, bir dosya tutamacı - yönettiğinde ve üye üye kopyalamanın yanlış olacağı durumda kendinizinkini yazmanız gerekir. Standart imza, atamaların zincirlenebilmesi için (a = b = c) *this'i referansla döndürür:
Bu kısa fonksiyonda iki tuzak yaşar. Birincisi, kendine atama kontrolü if (this == &other): o olmadan a = a, delete[] data yapar ve ardından az önce serbest bırakılan other.data'dan okur - tanımsız davranış. İkincisi, sıra önemlidir - elle yazılmış bir sürümde, yenisini güvenle kopyalamadan önce eski tamponu silmemelisiniz (gerçek bir uygulama genellikle önce bellek ayırır veya copy-and-swap deyimini kullanır, böylece başarısız bir bellek ayırma nesneyi bozulmamış bırakır).
Daha geniş bir tuzak: operatörleri şaşırtıcı şekillerde aşırı yüklemeyin. Sol işlenenini gizlice değiştiren bir operator+ veya simetrik olmayan bir operator==, her okuyucunun kafasını karıştırır ve olağan anlamları varsayan standart kütüphane kodunu bozar. Operatörleri yalnızca işlem, türünüz için gerçekten "toplama benzeri" veya "eşitlik benzeri" olduğunda aşırı yükleyin.
Sıradaki: Erişim Belirteçleri
Her örneğin veri üyelerini private tutup davranışı küçük bir genel yüzey - yapıcılar, operatörler ve birkaç metot - aracılığıyla nasıl açığa çıkardığına dikkat edin. Dış dünyaya görünür olan ile sınıfın içinde gizli kalan arasındaki bu sınır, erişim belirteçleri ile denetlenir: public, private ve protected. Sırada her birinin tam olarak neye izin verdiğine, genel metotlu private verinin neden iyi kapsülleme için varsayılan olduğuna ve protected'in kalıtıma nasıl uyduğuna bakacağız.
Sıkça Sorulan Sorular
C++'ta operatör aşırı yükleme nedir?
Operatör aşırı yükleme, +, == veya << gibi yerleşik operatörlerin kendi türleriniz için ne anlama geldiğini tanımlamanıza olanak tanır. Özel adlı bir fonksiyon yazarsınız - operator+, operator== vb. - ve operatör sizin sınıfınızın işlenenleriyle göründüğünde derleyici bunu çağırır. string + string ifadesinin birleştirme yapması ve cout << obj ifadesinin özel bir nesneyi yazdırması bu sayededir.
C++'ta operatörler üye fonksiyon mu yoksa üye olmayan (friend) fonksiyon mu olmalı?
Sol işlenen kendi sınıfınız olduğunda ve dönüşüm gerekmediğinde bir üye fonksiyon kullanın (ör. +=, [], ()). Sol işlenen yerleşik bir tür olabildiğinde veya her iki tarafta da simetrik dönüşümler istediğinizde bir üye olmayan fonksiyon (genellikle friend) kullanın; operator<< için bu zorunludur, çünkü sol işlenen sizin sınıfınız değil bir std::ostream'dir.
Hangi C++ operatörleri aşırı yüklenemez?
:: (kapsam çözümleme), . (üye erişimi), .* (üye işaretçisi erişimi), ?: (üçlü) ve sizeof operatörlerini aşırı yükleyemezsiniz. Ayrıca tamamen yeni operatörler icat edemez veya bir operatörün işlenen sayısını ya da önceliğini değiştiremezsiniz - + ister int toplasın ister sizin Vector2 türünüzü, her zaman aynı öncelikli ikili bir operatördür.