Function Overloading ve Default Parametreler
Günlük hayatta "aç" kelimesini düşün. "Kapıyı aç", "dosyayı aç", "şişeyi aç" — aynı fiil ama farklı bağlamlarda farklı anlamlara geliyor. C++'ta da benzer bir şey yapabilirsin: aynı isimde birden fazla fonksiyon tanımlayabilirsin. Derleyici hangisini çağıracağını parametrelere bakarak anlar. Buna function overloading (fonksiyon aşırı yükleme) denir.
Bu derste overloading kurallarını, default parametreleri, ikisi arasındaki tercihi ve derleyicinin perde arkasında nasıl çalıştığını öğreneceksin.
Function Overloading — Aynı İsim, Farklı İmza
C++ aynı isimde birden fazla fonksiyon tanımlamana izin verir — yeter ki parametre listeleri farklı olsun. Derleyici hangi fonksiyonu çağıracağını, çağrıdaki argüman sayısına ve tiplerine bakarak karar verir.
#include <iostream>
using namespace std;
// Üç farklı "topla" fonksiyonu
int topla(int a, int b) {
return a + b;
}
double topla(double a, double b) {
return a + b;
}
int topla(int a, int b, int c) {
return a + b + c;
}
int main() {
cout << topla(3, 5) << endl; // int versiyonu → 8
cout << topla(3.14, 2.72) << endl; // double versiyonu → 5.86
cout << topla(1, 2, 3) << endl; // üç parametreli → 6
return 0;
}Derleyici topla(3, 5) gördüğünde "iki int parametre alan topla hangisi?" diye bakar ve int topla(int, int)'i seçer. topla(3.14, 2.72) gördüğünde ise double topla(double, double)'ı seçer.
Overloading Kuralları
Fonksiyonların ayırt edilebilmesi için parametre listelerinin farklı olması gerekir. Farklılık şu şekillerde olabilir:
Parametre sayısı farklı
Parametre tipleri farklı
Parametre sırası farklı (farklı tipler için)
// GEÇERLI overloading örnekleri
void yazdir(int x); // 1 int
void yazdir(double x); // 1 double
void yazdir(int x, int y); // 2 int
void yazdir(string s); // 1 string
void yazdir(int x, string s); // int + string
void yazdir(string s, int x); // string + int (sıra farklı)⚠️ Dikkat: Sadece dönüş tipi farklı olan fonksiyonlar overload sayılmaz! Derleyici çağrı noktasında dönüş tipine bakarak karar veremez.
// DERLEME HATASI — sadece dönüş tipi farklı
int hesapla(int x);
double hesapla(int x); // HATA! Parametre listesi aynıOverloading ile Tip Dönüşümü
Derleyici tam eşleşme bulamazsa, örtük tip dönüşümü (implicit conversion) uygulayarak uygun bir overload arar. Bu bazen beklenmedik sonuçlar doğurabilir.
#include <iostream>
using namespace std;
void goster(int x) {
cout << "int: " << x << endl;
}
void goster(double x) {
cout << "double: " << x << endl;
}
int main() {
goster(42); // Tam eşleşme: int
goster(3.14); // Tam eşleşme: double
goster('A'); // char → int dönüşümü: int (65)
goster(3.14f); // float → double dönüşümü: double
return 0;
}char tipi int'e yakın olduğu için goster('A') çağrısında int versiyonu seçilir. Eğer derleyici hangi dönüşümün yapılacağına karar veremezse belirsizlik hatası (ambiguity error) verir.
void islem(float x);
void islem(double x);
// islem(42); // HATA — int hem float'a hem double'a dönüşebilir
// Derleyici hangisini seçeceğini bilemezPratik Overloading Örneği
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Farklı tipleri yazdıran overloaded fonksiyon ailesi
void yazdir(int deger) {
cout << "[int] " << deger << endl;
}
void yazdir(const string& deger) {
cout << "[string] " << deger << endl;
}
void yazdir(const vector<int>& degerler) {
cout << "[vector] ";
for (const auto& v : degerler) {
cout << v << " ";
}
cout << endl;
}
void yazdir(const string& etiket, int deger) {
cout << "[etiketli] " << etiket << ": " << deger << endl;
}
int main() {
yazdir(42);
yazdir("Merhaba Dünya"s);
yazdir(vector<int>{1, 2, 3, 4, 5});
yazdir("Skor", 95);
return 0;
}Aynı yazdir ismi, farklı tipler ve parametre sayıları için farklı davranışlar sergiler. Çağıran kod temiz ve tutarlı kalır.
Default Parametreler — Varsayılan Değerler
Bazen bir fonksiyonun parametresine çoğu zaman aynı değeri geçirirsin. Default parametre ile bu değeri önceden tanımlarsın — çağırırken o parametreyi yazmasanız bile otomatik kullanılır.
#include <iostream>
#include <string>
using namespace std;
void selamla(const string& isim, const string& selam = "Merhaba") {
cout << selam << ", " << isim << "!" << endl;
}
int main() {
selamla("Ali"); // Merhaba, Ali!
selamla("Ayşe", "Günaydın"); // Günaydın, Ayşe!
return 0;
}selam parametresinin default değeri "Merhaba". Çağrırken ikinci argüman verilmezse otomatik olarak "Merhaba" kullanılır. Verilirse verilen değer geçerli olur.
Default Parametre Kuralları
Sağdan sola sıralama: Default parametreler sağdan başlayarak tanımlanmalı. Ortada boşluk bırakamazsın.
// DOĞRU
void fonk(int a, int b = 5, int c = 10);
// YANLIŞ — ortada default olan, sağda olmayan
void fonk(int a, int b = 5, int c); // DERLEME HATASIPrototip veya tanımda — ikisinde birden default değer yazma, sadece birinde yaz. Genellikle prototipte (header'da) yazılır.
// Prototipte default değer (tercih edilen yöntem)
void ayarla(int genislik, int yukseklik = 600, int derinlik = 32);
// Tanımda default değer YOK
void ayarla(int genislik, int yukseklik, int derinlik) {
cout << genislik << "x" << yukseklik << " @ " << derinlik << "bit" << endl;
}#include <iostream>
using namespace std;
double faizHesapla(double anapara, double oran = 0.05, int yil = 1) {
double sonuc = anapara;
for (int i = 0; i < yil; i++) {
sonuc *= (1 + oran);
}
return sonuc;
}
int main() {
// Sadece anapara — oran %5, süre 1 yıl
cout << faizHesapla(1000) << endl; // 1050
// Anapara + oran — süre 1 yıl
cout << faizHesapla(1000, 0.10) << endl; // 1100
// Tüm parametreler
cout << faizHesapla(1000, 0.10, 3) << endl; // 1331
return 0;
}💡 İpucu: Default parametreler, fonksiyon çağrısını basitleştirir. En sık kullanılan değerleri default yaparak çağıran kodun gereksiz detay yazmasını önlersin. Ama çok fazla default parametre fonksiyonun ne yaptığını belirsizleştirebilir — 2-3 default yeterli.
Overloading vs Default Params — Hangisi Ne Zaman?
Her iki yaklaşım da "aynı fonksiyonu farklı şekillerde çağırma" amacına hizmet eder. Ama farklı senaryolarda farklı biri tercih edilir.
Default Parametreler Tercih Et
Parametreler aynı tipte ve mantıksal bir varsayılan değer varsa:
// Default parametre — temiz ve basit
void log(const string& mesaj, const string& seviye = "INFO") {
cout << "[" << seviye << "] " << mesaj << endl;
}
log("Sistem başladı"); // [INFO] Sistem başladı
log("Disk dolu!", "WARNING"); // [WARNING] Disk dolu!Overloading Tercih Et
Parametrelerin tipleri tamamen farklıysa veya farklı mantık gerekiyorsa:
// Overloading — farklı tipler, farklı mantık
string formatla(int sayi) {
return to_string(sayi);
}
string formatla(double sayi) {
// Ondalık hassasiyet ayarı gerekli
char buf[32];
snprintf(buf, sizeof(buf), "%.2f", sayi);
return string(buf);
}
string formatla(const string& metin) {
return "\"" + metin + "\"";
}Karşılaştırma Tablosu
| Durum | Tercih |
|---|---|
| Aynı mantık, bazı parametreler opsiyonel | Default parametre |
| Farklı tipler, farklı işlem mantığı | Overloading |
| Her çağrıda aynı varsayılan değer | Default parametre |
| Parametre tipi çağrıya göre değişiyor | Overloading |
| API'yi basit tutmak istiyorsun | Default parametre |
| Tip güvenliği kritik | Overloading |
⚠️ Dikkat: Overloading ve default parametreleri birlikte kullanırken belirsizlik oluşabilir:
void islem(int a, int b = 0);
void islem(int a);
// islem(5); // HATA — hangisini çağıracağını bilemez!İkisi de islem(5) çağrısına uyuyor — derleyici karar veremez. Bu durumdan kaçın.
Name Mangling — Perde Arkası
Derleyici aynı isimde birden fazla fonksiyonu nasıl ayırt ediyor? Cevap: name mangling (isim karıştırma). Derleyici her fonksiyona parametre tiplerine göre benzersiz bir iç isim verir.
int topla(int, int); // Derleyicinin gördüğü: _Z5toplaii
double topla(double, double); // Derleyicinin gördüğü: _Z5topladd
int topla(int, int, int); // Derleyicinin gördüğü: _Z5toplaiiiBu isimler derleyiciye özeldir (GCC, Clang, MSVC farklı isimler üretir). Ama mantık aynı: fonksiyon adı + parametre tipleri = benzersiz tanımlayıcı.
extern "C" — Mangling'i Devre Dışı Bırakma
C kütüphaneleri name mangling kullanmaz. C++ kodundan C fonksiyonlarını çağırmak istiyorsan extern "C" kullanırsın:
// C fonksiyonu — name mangling yok
extern "C" {
void c_fonksiyonu(int x);
}
// C++ fonksiyonu — name mangling var
void cpp_fonksiyonu(int x);Bu konuyu ileride daha detaylı göreceksin. Şimdilik name mangling'in overloading'i mümkün kılan mekanizma olduğunu bilmek yeterli.
Gerçek Dünya Örneği — Loglama Sistemi
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
class Logger {
public:
// Overloaded log fonksiyonları
static void log(const string& mesaj) {
log(mesaj, "INFO");
}
static void log(const string& mesaj, const string& seviye) {
time_t simdi = time(nullptr);
char zaman[20];
strftime(zaman, sizeof(zaman), "%H:%M:%S",
localtime(&simdi));
cout << "[" << zaman << "] [" << seviye << "] "
<< mesaj << endl;
}
static void log(int kod, const string& mesaj) {
log("Kod " + to_string(kod) + ": " + mesaj, "ERROR");
}
};
int main() {
Logger::log("Sistem başlatıldı");
Logger::log("Disk alanı düşük", "WARNING");
Logger::log(404, "Sayfa bulunamadı");
return 0;
}Aynı log fonksiyonu üç farklı şekilde çağrılabiliyor: sadece mesaj, mesaj + seviye, hata kodu + mesaj. Çağıran kod temiz kalıyor, detaylar fonksiyonların içinde gizli.
Özet
Function overloading aynı isimde, farklı parametre listeli birden fazla fonksiyon tanımlamadır — derleyici doğru olanı argümanlara bakarak seçer
Overload farkı parametre sayısı, tipi veya sırası ile olur — sadece dönüş tipi farkı yetmez
Default parametreler çağrıda verilmezse kullanılan varsayılan değerlerdir — sağdan sola sırayla tanımlanır
Overloading farklı tipler ve farklı mantık için, default parametreler aynı mantıkla opsiyonel argümanlar için tercih edilir
İkisini birlikte kullanırken belirsizlik oluşmamasına dikkat et
Name mangling derleyicinin overloaded fonksiyonları ayırt etmek için kullandığı iç isimlendirme mekanizmasıdır
AI Asistan
Sorularını yanıtlamaya hazır