Operatör Overloading
C++'ın en güçlü özelliklerinden biri operatör overloading (operatör aşırı yükleme). +, -, ==, << gibi operatörlerin kendi sınıflarınla nasıl çalışacağını sen tanımlarsın. Böylece nesnelerin tıpkı temel türler gibi doğal bir sözdizimi ile kullanılmasını sağlarsın.
Bunu bir evrensel adaptör gibi düşün. Prizin şekli değişmiyor (operatör aynı) ama arkadaki kablolama (implementasyon) cihaza göre farklı. + operatörü iki int toplamayı bilir ama senin Vektor sınıfın için ne yapacağını bilmez — onu sen tanımlarsın.
Neden Operatör Overloading?
Operatör overloading olmadan:
Vektor a(1, 2);
Vektor b(3, 4);
Vektor c = a.topla(b); // Çirkin
if (a.esitMi(b)) { ... } // Doğal değil
cout << a.toString() << endl; // ZahmetliOperatör overloading ile:
Vektor a(1, 2);
Vektor b(3, 4);
Vektor c = a + b; // Doğal
if (a == b) { ... } // Sezgisel
cout << a << endl; // Temizİkinci versiyon çok daha okunabilir. Kod matematiksel ifadeler gibi okunur.
Temel Kurallar
Overloading yaparken uyulması gereken kurallar:
Yeni operatör icat edemezsin — sadece mevcut C++ operatörlerini overload edebilirsin
Bazı operatörler overload edilemez:
::,.,.*,?:,sizeofÖncelik ve birleşim yönü değişmez —
*her zaman+'dan önce gelirEn az bir operand kullanıcı tanımlı tür olmalı —
int + int'i değiştiremezsinAnlamlı ol —
+ile silme işlemi yapma, sezgiselliği koru
Aritmetik Operatörler (+, -, *, /)
Üye Fonksiyon Olarak
#include <iostream>
using namespace std;
class Vektor2D {
private:
double x, y;
public:
Vektor2D(double x = 0, double y = 0) : x(x), y(y) {}
// + operatörü (üye fonksiyon olarak)
Vektor2D operator+(const Vektor2D& diger) const {
return Vektor2D(x + diger.x, y + diger.y);
}
// - operatörü
Vektor2D operator-(const Vektor2D& diger) const {
return Vektor2D(x - diger.x, y - diger.y);
}
// Skaler çarpım (vektor * sayi)
Vektor2D operator*(double skaler) const {
return Vektor2D(x * skaler, y * skaler);
}
// Tekli eksi (negation): -v
Vektor2D operator-() const {
return Vektor2D(-x, -y);
}
void yazdir() const {
cout << "(" << x << ", " << y << ")" << endl;
}
};
int main() {
Vektor2D a(3, 4);
Vektor2D b(1, 2);
Vektor2D c = a + b; // operator+ çağrılır
Vektor2D d = a - b; // operator- çağrılır
Vektor2D e = a * 2.0; // operator*(double) çağrılır
Vektor2D f = -a; // tekli eksi
c.yazdir(); // (4, 6)
d.yazdir(); // (2, 2)
e.yazdir(); // (6, 8)
f.yazdir(); // (-3, -4)
return 0;
}+= ve -= Operatörleri (Compound Assignment)
#include <iostream>
using namespace std;
class Vektor2D {
private:
double x, y;
public:
Vektor2D(double x = 0, double y = 0) : x(x), y(y) {}
// += kendini değiştirip referans döndürür
Vektor2D& operator+=(const Vektor2D& diger) {
x += diger.x;
y += diger.y;
return *this;
}
// + operatörünü += üzerinden tanımla (DRY prensibi)
Vektor2D operator+(const Vektor2D& diger) const {
Vektor2D sonuc = *this; // kopyala
sonuc += diger; // += kullan
return sonuc;
}
void yazdir() const {
cout << "(" << x << ", " << y << ")" << endl;
}
};
int main() {
Vektor2D a(1, 2);
Vektor2D b(3, 4);
a += b;
a.yazdir(); // (4, 6)
Vektor2D c = Vektor2D(1, 1) + Vektor2D(2, 2);
c.yazdir(); // (3, 3)
return 0;
}💡 İpucu:
+operatörünü tanımlarken, önce+=yaz ve+'yı onun üzerine kur. Bu kod tekrarını önler ve tutarlılık sağlar.
Karşılaştırma Operatörleri (==, <, <=>)
== ve != Operatörleri
#include <iostream>
using namespace std;
class Nokta {
private:
double x, y;
public:
Nokta(double x = 0, double y = 0) : x(x), y(y) {}
bool operator==(const Nokta& diger) const {
return x == diger.x && y == diger.y;
}
bool operator!=(const Nokta& diger) const {
return !(*this == diger); // == üzerinden tanımla
}
};
int main() {
Nokta a(3, 4);
Nokta b(3, 4);
Nokta c(1, 2);
cout << (a == b) << endl; // 1 (true)
cout << (a != c) << endl; // 1 (true)
return 0;
}< Operatörü ve Sıralama
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Ogrenci {
private:
string isim;
double ortalama;
public:
Ogrenci(string i, double o) : isim(i), ortalama(o) {}
// Ortalamaya göre sırala (büyükten küçüğe)
bool operator<(const Ogrenci& diger) const {
return ortalama > diger.ortalama; // büyük olan önce gelsin
}
bool operator==(const Ogrenci& diger) const {
return isim == diger.isim && ortalama == diger.ortalama;
}
friend ostream& operator<<(ostream& os, const Ogrenci& o) {
os << o.isim << " (" << o.ortalama << ")";
return os;
}
};
int main() {
vector<Ogrenci> sinif = {
{"Ali", 85.5}, {"Veli", 92.0}, {"Ayşe", 78.0}, {"Fatma", 95.5}
};
sort(sinif.begin(), sinif.end()); // operator< kullanır
for (const auto& o : sinif) {
cout << o << endl;
}
// Fatma (95.5)
// Veli (92)
// Ali (85.5)
// Ayşe (78)
return 0;
}Spaceship Operatörü — <=> (C++20)
C++20 ile gelen three-way comparison operator (<=>), tüm karşılaştırma operatörlerini tek seferde tanımlamanı sağlar. Adı "spaceship" çünkü <=> şekli bir uzay gemisine benzer.
#include <iostream>
#include <compare> // <=> için
using namespace std;
class Sicaklik {
private:
double derece;
public:
Sicaklik(double d) : derece(d) {}
// <=> tanımla — ==, !=, <, >, <=, >= hepsi otomatik gelir!
auto operator<=>(const Sicaklik& diger) const = default;
friend ostream& operator<<(ostream& os, const Sicaklik& s) {
os << s.derece << "°C";
return os;
}
};
int main() {
Sicaklik a(25.0);
Sicaklik b(30.0);
Sicaklik c(25.0);
cout << (a < b) << endl; // 1 (true)
cout << (a > b) << endl; // 0 (false)
cout << (a == c) << endl; // 1 (true)
cout << (a != b) << endl; // 1 (true)
cout << (a <= c) << endl; // 1 (true)
cout << (a >= b) << endl; // 0 (false)
return 0;
}Özel <=> Tanımlama
Eğer = default yetmiyorsa (farklı bir karşılaştırma mantığı istiyorsan) kendin yazabilirsin:
#include <iostream>
#include <compare>
#include <string>
using namespace std;
class Versiyon {
private:
int major, minor, patch;
public:
Versiyon(int ma, int mi, int p)
: major(ma), minor(mi), patch(p) {}
strong_ordering operator<=>(const Versiyon& diger) const {
if (auto cmp = major <=> diger.major; cmp != 0) return cmp;
if (auto cmp = minor <=> diger.minor; cmp != 0) return cmp;
return patch <=> diger.patch;
}
// <=> tanımlarsan == otomatik gelmez (= default değilse)
bool operator==(const Versiyon& diger) const {
return (*this <=> diger) == 0;
}
friend ostream& operator<<(ostream& os, const Versiyon& v) {
os << v.major << "." << v.minor << "." << v.patch;
return os;
}
};
int main() {
Versiyon v1(2, 1, 0);
Versiyon v2(2, 3, 1);
Versiyon v3(2, 1, 0);
cout << v1 << " < " << v2 << ": " << (v1 < v2) << endl; // 1
cout << v1 << " == " << v3 << ": " << (v1 == v3) << endl; // 1
return 0;
}💡 İpucu: C++20 kullanabiliyorsan
operator<=>ile başla. Tek tanımlama ile 6 karşılaştırma operatörü elde edersin. Basit sınıflarda= defaultyeterli olur.
<< Operatörü (ostream ile Yazdırma)
<< operatörünü overload ederek nesneyi cout ile doğrudan yazdırabilirsin. Bu operatör friend fonksiyon olarak tanımlanmalı çünkü sol taraftaki operand ostream nesnesi (sınıfımız değil):
#include <iostream>
#include <string>
using namespace std;
class Tarih {
private:
int gun, ay, yil;
public:
Tarih(int g, int a, int y) : gun(g), ay(a), yil(y) {}
// friend olarak tanımla — sol taraf ostream
friend ostream& operator<<(ostream& os, const Tarih& t) {
os << t.gun << "/" << t.ay << "/" << t.yil;
return os; // zincirleme çıktı için ostream'i döndür
}
// >> ile giriş (istream)
friend istream& operator>>(istream& is, Tarih& t) {
char ayrac;
is >> t.gun >> ayrac >> t.ay >> ayrac >> t.yil;
return is;
}
};
int main() {
Tarih bugun(19, 2, 2026);
cout << "Bugün: " << bugun << endl; // Bugün: 19/2/2026
// Zincirleme çalışır
Tarih yarin(20, 2, 2026);
cout << bugun << " -> " << yarin << endl;
// Giriş
cout << "Tarih girin (GG/AA/YYYY): ";
Tarih t(0, 0, 0);
// cin >> t; // Kullanıcıdan giriş alır
// cout << "Girilen: " << t << endl;
return 0;
}[] ve () Operatörleri
[] — İndeksleme Operatörü
#include <iostream>
#include <stdexcept>
using namespace std;
class DinamikDizi {
private:
int* veri;
int boyut;
public:
DinamikDizi(int n) : boyut(n) {
veri = new int[n](); // sıfırla
}
~DinamikDizi() { delete[] veri; }
// [] operatörü — referans döndür (okuma + yazma)
int& operator[](int indeks) {
if (indeks < 0 || indeks >= boyut) {
throw out_of_range("Indeks sınır dışı!");
}
return veri[indeks];
}
// const versiyon — sadece okuma
const int& operator[](int indeks) const {
if (indeks < 0 || indeks >= boyut) {
throw out_of_range("Indeks sınır dışı!");
}
return veri[indeks];
}
int size() const { return boyut; }
};
int main() {
DinamikDizi d(5);
// Yazma
d[0] = 10;
d[1] = 20;
d[2] = 30;
// Okuma
for (int i = 0; i < d.size(); i++) {
cout << "d[" << i << "] = " << d[i] << endl;
}
// Sınır kontrolü
try {
d[10] = 99;
} catch (const out_of_range& e) {
cout << "Hata: " << e.what() << endl;
}
return 0;
}() — Fonksiyon Çağrı Operatörü (Functor)
() operatörünü overload eden sınıflara functor (fonksiyon nesnesi) denir. Nesne bir fonksiyon gibi çağrılabilir:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class CiftMi {
public:
bool operator()(int sayi) const {
return sayi % 2 == 0;
}
};
class Toplayici {
private:
int toplam;
public:
Toplayici() : toplam(0) {}
void operator()(int sayi) {
toplam += sayi;
}
int sonuc() const { return toplam; }
};
int main() {
CiftMi cift_mi;
cout << cift_mi(4) << endl; // 1 (true)
cout << cift_mi(7) << endl; // 0 (false)
// STL ile kullanım
vector<int> sayilar = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int cift_sayisi = count_if(sayilar.begin(), sayilar.end(), CiftMi());
cout << "Çift sayı adedi: " << cift_sayisi << endl; // 5
// Toplayıcı
Toplayici top;
for (int s : sayilar) {
top(s);
}
cout << "Toplam: " << top.sonuc() << endl; // 55
return 0;
}⚠️ Dikkat: Functor'lar C++11'den önce lambda yerine kullanılıyordu. Bugün basit durumlarda lambda tercih edilir ama functor'lar state tutabilme avantajı sayesinde hâlâ kullanışlıdır.
Üye Fonksiyon vs friend Fonksiyon
Operatörü üye fonksiyon olarak mı yoksa friend olarak mı tanımlayacağın önemli bir tasarım kararı:
#include <iostream>
using namespace std;
class Sayi {
private:
int deger;
public:
Sayi(int d) : deger(d) {}
// ÜYE FONKSİYON: sol taraf her zaman Sayi nesnesi
Sayi operator+(const Sayi& diger) const {
return Sayi(deger + diger.deger);
}
// FRIEND: sol taraf Sayi olmak zorunda değil
friend Sayi operator*(int skaler, const Sayi& s);
friend ostream& operator<<(ostream& os, const Sayi& s);
int getDeger() const { return deger; }
};
// 3 * sayi şeklinde kullanım — sol taraf int
Sayi operator*(int skaler, const Sayi& s) {
return Sayi(skaler * s.deger);
}
ostream& operator<<(ostream& os, const Sayi& s) {
os << s.deger;
return os;
}
int main() {
Sayi a(5);
Sayi b(3);
Sayi c = a + b; // üye fonksiyon: a.operator+(b)
Sayi d = 3 * a; // friend fonksiyon: operator*(3, a)
// Sayi e = a * 3; // Bu üye olarak tanımlanmadı — farklı imza gerekir
cout << c << endl; // 8
cout << d << endl; // 15
return 0;
}Hangi Yolu Seçmeli?
| Operatör | Önerilen Yol | Neden |
|---|---|---|
=, [], (), -> | Üye fonksiyon | C++ kuralı gereği üye olmalı |
+=, -=, *= | Üye fonksiyon | Sol taraf değişiyor |
+, -, *, / | Her ikisi de olur | Simetrik istiyorsan friend |
==, <, <=> | Üye fonksiyon | C++20'de = default desteği |
<<, >> | friend fonksiyon | Sol taraf ostream/istream |
Tam Örnek: Kesir (Fraction) Sınıfı
#include <iostream>
#include <numeric> // gcd (C++17)
using namespace std;
class Kesir {
private:
int pay, payda;
void sadeles() {
int ortakBolen = gcd(abs(pay), abs(payda));
pay /= ortakBolen;
payda /= ortakBolen;
if (payda < 0) { pay = -pay; payda = -payda; }
}
public:
Kesir(int p = 0, int pd = 1) : pay(p), payda(pd) {
if (payda == 0) throw invalid_argument("Payda 0 olamaz!");
sadeles();
}
// Aritmetik
Kesir operator+(const Kesir& d) const {
return Kesir(pay * d.payda + d.pay * payda, payda * d.payda);
}
Kesir operator-(const Kesir& d) const {
return Kesir(pay * d.payda - d.pay * payda, payda * d.payda);
}
Kesir operator*(const Kesir& d) const {
return Kesir(pay * d.pay, payda * d.payda);
}
Kesir operator/(const Kesir& d) const {
return Kesir(pay * d.payda, payda * d.pay);
}
// Karşılaştırma
bool operator==(const Kesir& d) const {
return pay == d.pay && payda == d.payda;
}
bool operator<(const Kesir& d) const {
return pay * d.payda < d.pay * payda;
}
// Yazdırma
friend ostream& operator<<(ostream& os, const Kesir& k) {
if (k.payda == 1) os << k.pay;
else os << k.pay << "/" << k.payda;
return os;
}
};
int main() {
Kesir a(1, 2); // 1/2
Kesir b(1, 3); // 1/3
cout << a << " + " << b << " = " << (a + b) << endl; // 5/6
cout << a << " - " << b << " = " << (a - b) << endl; // 1/6
cout << a << " * " << b << " = " << (a * b) << endl; // 1/6
cout << a << " / " << b << " = " << (a / b) << endl; // 3/2
cout << a << " == " << b << ": " << (a == b) << endl; // 0
cout << a << " < " << b << ": " << (a < b) << endl; // 0
Kesir c(2, 4); // 2/4 = 1/2 (sadeleşir)
cout << a << " == " << c << ": " << (a == c) << endl; // 1
return 0;
}Overloading'de Dikkat Edilmesi Gerekenler
Sezgisel ol:
+toplama yapsın,-çıkarma. Sürpriz olmasın.Tutarlı ol:
+tanımladıysan+=da tanımla.==tanımladıysan!=da tanımla.const doğruluğu: Nesneyi değiştirmeyen operatörler
constolmalı.Return tipi:
+yeni nesne döndürmeli,+=referans (*this).Self-assignment:
=operatöründeif (this != &diger)kontrolü yap.
⚠️ Dikkat: Operatör overloading güçlü bir araç ama kötüye kullanılabilir.
+ile veritabanı sorgusu çalıştıran veya*ile dosya silen bir sınıf yazmak mümkün ama büyük hata olur. Operatörler matematiksel veya mantıksal anlamlarına uygun kullanılmalıdır.
Özet
Operatör overloading, mevcut C++ operatörlerinin kullanıcı tanımlı türlerle çalışmasını sağlar. Kodun okunabilirliğini artırır.
Aritmetik operatörler (+, -, *, /) yeni nesne döndürür; compound assignment (+=, -=) referans döndürür.
+'yı+=üzerinden tanımlamak DRY prensibidir.Karşılaştırma operatörleri (==, <) bool döndürür. C++20'deki spaceship operatörü (
<=>) ile tek tanımlamayla 6 karşılaştırma operatörü elde edilir.<< operatörü ostream ile yazdırmak için friend fonksiyon olarak tanımlanır;
ostream&döndürerek zincirleme çıktı sağlar.[] operatörü indeksleme, () operatörü ise functor (fonksiyon nesnesi) oluşturmak için kullanılır.
Üye fonksiyon olarak tanımlama sol tarafın sınıf nesnesi olmasını gerektirir; friend fonksiyon daha esnek simetrik kullanım sağlar.
=,[],(),->zorunlu olarak üye fonksiyon olmalıdır.
AI Asistan
Sorularını yanıtlamaya hazır