"Enter"a basıp içeriğe geçin

Operatör Aşırı Yükleme

 

Aşırı yükleme, bir işlevi farklı girdi değerleri için çalışır hale getirmek demekti. Mesela fonksiyon aşırı yüklemesi ile bir fonksiyonun farklı sayıda ve türde parametreler ile çalışmasını sağlıyorduk.
Operatör aşırı yükleme de, operatörlere yeni anlamlar kazandırıp işlevlerini  değiştirerek kendi tipimize uyarlamamızı ve işlemleri kolaylaştırmamızı sağlar. Bu, temel tipteki anlamlarını değiştirmek demek değildir. Kendi tipimizde çalışmayan operatörleri çalışır hale getirmektir.

Operatör aşırı yükleme gerekli midir?

Aslında aşırı yükleme olmadan da işimizi yapamaz mıydık? Evet yapardık. Ama daha karmaşık olacağı için yazma hızımızı etkilerdi ve zorluğu artardı. Operatör aşırı yüklemenin mantığı da aynı şekildedir. Aslında yapacağımız her şeyi fonksiyonlarla da halledebilirdik. Ancak operatörlere kazandırdığımız yeni anlamlarla kodumuzu sadeleştirmiş oluruz. Okunabilirlik artar ve yazmamızı büyük oranda kolaylaştırırız.

Mesela string sınıfında + operatörü aşırı yüklenmişdir. Buna gerek var mıydı? Toplama fonksiyonu yazıp topla(str1,str2); diyerek iki stringi birleştirebilirdik. Ancak str1+str2 yazmak çok daha sade ve hızlı olur. Okunurluğu da arttırır. En basit ve kolay örnek olarak bunu verebiliriz.

Operatör aşırı yükleme kuralları

Operatörleri aşırı yüklerken dikkat etmemiz gereken belli başlı kurarlara bakalım:

Yukarıda görüldüğü gibi, standart türlerde (int ,double gibi) tanımlı olan + operatörünü sonradan çıkmış olan string türüne uyarlayabiliyoruz.
İlk dikkat etmemiz gereken şey, operatörü aşırı yüklerken, diğer kullanımlarına, çalışma mantıklarına uyumlu şekilde aşırı yüklemeliyiz. Mesela + operatörünü birşey yazdırmak için kullanmak mantıklı olmaz. Teknik olarak mümkündür, ancak kodu okuyan başka bir yazılımcının anlamasını zorlaştırır. Yani kalıplaşmış ifadeleri kullanmaya dikkat etmek kodun başkaları tarafından anlaşılmasını büyük oranda arttırır.
İkinci  olarak, bazı kilit operatörleri aşırı yükleyemiyoruz bunlar:   .  .*  :: ve ?:  operatörleridir. Bu operatörlerin anlamları  sabittir ve değiştirilemezler. Ayrıca dilde olmayan yeni bir operatör de tanımlanamaz.

Operant nedir?

Operatörlerin sağında ve solunda değişkenler bulunabilir. İşlemleri de bu sağındaki ve solundaki değişkenlere göre yapar. Bu sağdaki ve soldaki değişkenlere operant denir. Operatörün sahibi soldaki  operanttır. Nesnemiz soldaki operant olur ve sağdaki de parametre olarak aşırı yüklenen fonksiyonumuza gelir. Aşırı yükleme fonksiyonu soldakinin üye fonksiyonudur.

Şimdi 2 kompleks sayıları tutan bir sınıfımız olsun. Bu sınıf için + operatörünü aşırı yükleyelim

Görüldüğü gibi, aslında bir üye fonksiyon gibi. Derleyici k1+k2  gördüğünde k1 ve k2 değişkenlerinin tipine bakarak aşırı yüklenmiş bir operatör olduğunu anlıyor ve buraya fonksiyonun gerçek imzası olan kompleks operator+( kompleks& k) koyuyor.

 

 

Sol operanttaki nesnemiz için üye fonksiyon yani operator+( kompleks& k) çağrılıyor ve sağ operant nesnesi parametre olarak (k değişkenine) geliyor. Sonuç olarak bir değer dönüyor.

Bu  fonksiyonu global fonksiyon olarak da yazabilirdik ama friend ile yetki vermemiz gerekirdi. Bu durumda sol operant da parametre olarak gelirdi.
kompleks operator+(kompleks& a, kompleks& b)

Bunu biraz daha açalım

Operatörlerin friend yapılması ve global fonksiyon olarak tanımı

Bazı durumlarda operatörün bizim sınıfımıza ait olmayan bir nesne için çağrılmasını isteyebiliriz. Yani operatörün solundaki operant, bizim olmayan bir nesne olabilir ve bizim private üyelerimize erişebilmesi gerekebilir.

Mesela << >> operatörleri, iostream kütüphanelerinde tanımlıdır. Ve siyah ekrana da yazdırabilmemizi sağlar. Ama bu operatörler, iostream içinde sadece temel tiplerde çalışacak şekilde aşırı yüklenmiştir ve cin, cout iostream’e ait nesnelerdir. Bu nesneler sol tarafta oldukları için operatörün sahibi onlardır. Sol taraftaki de parametre olarak gelmelidir.

Eğer temel tipte olmayan bir değişkeni, yani sınıfımızın private bir üyesini cin ve cout’a yollamak istiyorsak, bu private üyelere erişim izni sağlamalıyız. Çünkü başka bir sınıf (bu örnekte cin, cout nesnesi) normalde bizim private üyelerimize ulaşamaz. Bunun için de nesnesini kullanacağımız sınıfı o fonksiyon için friend  olarak tanımlamalıyız. Ve soldaki üye bizim nesnemiz olmadığı için hem sol hem de sağ operantlar parametre olarak fonksiyona gelmelidir. Sağ operantın (yani kendi sınıfımızın) private üyelerini kullanacağımızdan sınıfın içinde friend olarak tanımlamalıyız. Ve aşırı yükleme fonksiyonumuz da global olmalıdır.

Operatör Aşırı Yükleme Sağ Sol Kavramları

Sağ sol kavramlarını biraz daha açalım. Mesela a+b operatörünün sahibinin soldaki a nesnesi olduğunu, sağdakinin de parametre olduğunu söylemiştik. Aslında derleyici a+b gördüğünde a.operator+(b) gibi okur.

Yani a nesnesinin operator+ üye  fonksiyonunu b parametresi ile çağırır. Sağ sol mevzusunun kaynağı aslında bu 😊

Buraya kadar gördüklerimiz 2 operantlı operatörlerdi. 1 ve 3 operantlı operatörler de vardır ama 3 opereantı olan tek operatör 😕 Olduğu ve aşırı yüklenemeyen bir operatör olduğu için ona girmeyeceğim

Şimdi 1 operantı olan operatörler için, hem de sağ sol olayı için bir örnek yapalım. Kompleks sayı sınıfımıza geri  dönelim:

Öncelikle prefix ve postfix’in yani ++i ve i++’nin ne olduğunu hatırlayalım. Eğer i++ dersek, önce i’yi kullanıp sonra arttırıyordu. ++i dersek, i arttırılıp bu artmış hali ile işlem yapılıyordu.

i+=k da i sayısını k kadar arttırıyordu. Şimdi bunları karmaşık sayılara uyarlayalım

Çalıştır

 

 

 

Bu koddan çıkaracağımız bir diğer sonuç da, prefix operatörü postfixden daha hızlıdır. Çünkü postfix yaparken temp bir değişken oluşturmamız gerekir ve bu da büyük döngülerde ciddi bir sıkıntıya sebep olur

    Bir cevap yazın

    E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir