← Kursa Dön
📄 Text · 12 min

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:

  1. Parametre sayısı farklı

  2. Parametre tipleri farklı

  3. 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 bilemez

Pratik 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ı

  1. 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 HATASI
  1. Prototip 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

DurumTercih
Aynı mantık, bazı parametreler opsiyonelDefault parametre
Farklı tipler, farklı işlem mantığıOverloading
Her çağrıda aynı varsayılan değerDefault parametre
Parametre tipi çağrıya göre değişiyorOverloading
API'yi basit tutmak istiyorsunDefault parametre
Tip güvenliği kritikOverloading

⚠️ 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üğü: _Z5toplaiii

Bu 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