> Default Arguments: >> Derleyici, DERLEME ZAMANINDA ilgili fonksiyonun bildirimine bakarak, ÇALIŞMA ZAMANINDA sanki o parametreleri de çağırmış gibi davranıyor. YANİ BU MEKANİZMA DERLEME ZAMANINA İLİŞKİN. YANİ ÇALIŞMA ZAMANI İLE HİÇ BİR İLİŞKİSİ YOKTUR. * Örnek 1, // some code here... int foo(int x = 1, int y = 2, int z = 3); int main() { foo(); // Derleyici tarafından DERLEME ZAMANINDA bu çağrı şuna çevrilir => foo(1,2,3); foo(5); // Derleyici tarafından DERLEME ZAMANINDA bu çağrı şuna çevrilir => foo(5,2,3); foo(10, 20); // Derleyici tarafından DERLEME ZAMANINDA bu çağrı şuna çevrilir => foo(10,20,3); foo(200, 300, 400); // Derleyici tarafından DERLEME ZAMANINDA bu çağrı şuna çevrilir => foo(200,300,400); } >> Fonksiyon bildirimlerinde/tanımlamalarında eğer 'default argument' kullanılmış ise (varsa) devamındaki parametrelerin de 'default argument' olmaları zorunlu. Aksi halde sentaks hatası. * Örnek 1, // some code here... int foo(int, double, char x = 'a'); // LEGAL int func(int, double d = 2.3, char x); // SENTAKS HATASIDIR. >> Birden fazla 'default argument' barındıran fonksiyonları çağırırken, 'default argument' alan parametreleri bir boş/bir dolu şekilde parametre geçemeyiz. * Örnek 1, // some code here... int foo(int i = 1, double d = 2.3, char x = 'a'); // LEGAL int main() { foo(, 2, ); // SENTAKS HATASIDIR. } >> 'default argument' YA FONKSİYONLARIN BİLDİRİMİNDE, Kİ TİPİK OLARAK ORALARDA, YA DA FONKSİYON TANIMLAMALARINDA KULLANILMALI. HER İKİSİNDE DE KULLANILAMAZ. >> 'default argument' olarak sadece bir 'constant expression' kullanılması zorunluluğu yoktur. 'global nesneler' de kullanılabilir. Bir sınırlama yoktur. * Örnek 1, // some codes here... int x = 10; int foo(int a, int b = x); // LEGAL int main() { foo(55); // Derleme zamanında derleyici tarafından buna dönüştürülür => foo(55, 10); } * Örnek 2, // some codes here... int foo(int = 100, int = 200); // LEGAL int func(int, int = foo()); // LEGAL int main() { func(77); // Derleme zamanında derleyici tarafından buna dönüştürülür => foo(77, foo(100, 200)); } * Örnek 3, // some codes here... int g1{}; int g2{}; int g3{13}; int foo(int& x = g1, int& y = g2, int* ptr = &g3); int main() { foo(); // Derleme zamanında derleyici tarafından buna dönüştürülür => foo(g1, g2, &g3); } >> 'default argument' olan ifadelerde daha önceki parametrelerin isimleri kullanılamaz. * Örnek 1, // some codes here... int foo(int a, int b = a); // SENTAKS HATASIDIR >> Eklemiş olduğunuz başlık dosyalarındaki fonksiyon bildirimlerinde yer alan fonksiyonların parametreleri her defasında yazmak yerine, onları 'redeclare' edebiliriz. Böylelikle derleyicimiz, bizim deklare ettiğimiz fonksiyonu çağıracaktır. * Örnek 1, // some code here... // include berker.h ile bize gelen fonksiyon void func(int, int, int); //Bizim 'redeclare' ettiğimiz fonksiyon ise void func(int, int, int = 3); int main() { func(1,2); // 6 func(1,2,0); // 0 } void func(int x, int y, int z) { std::cout << "x * y * z : " << x * y * z << "\n"; } >> Bir takım teknikler ile 'default argument' alan fonksiyonların bildirimleri kümülatif etki oluşturmaktadır. * Örnek 1, // some code here... void func(int, int, int = 56); // I void func(int x, int y = 12, int z); // II // Eğer sadece II numaralı fonksiyon bildirimi olsaydı sentaks hatası oluşacaktı. Çünkü 'default argument' alan // parametreden sonraki parametreler de 'default argument' olmak zorunda. Fakat derleyici önce I numaralı // fonksiyon bildirimi gördüğü için, ilgili fonksiyonun son parametresinin 'default argument' olduğunu öğreniyor. // Devamında da II numaralı fonksiyon bildirimi gördüğünden, ilgili fonksiyonun ikinci parametresinin de // 'default argument' olduğunu öğreniyor. Sonuç olarak derleyici, yukarıdaki bildirimleri bir nevi aşağıdaki // şekle getiriyor => void func(int x, y = 12, z = 56); int main() { func(1); // func(1, 12, 56); // x * y * z : 672 } void func(int x, int y, int z) { std::cout << "x * y * z : " << x * y * z << "\n"; } >> Bir diğer kullanım alanı da 'default argument' parametrelere göre fonksiyonun gövdesinde farklı işlemler yapılması: * Örnek 1, #include #include #include void processEntryDate(int day = -1, int mon = -1, int year = -1); // Fonksiyon bildirimi. int main() { processEntryDate(); // processEntryDate(-1, -1, -1); // 14-02-2022 processEntryDate(17); // processEntryDate(17, -1, -1); // 17-02-2022 processEntryDate(6,9); // processEntryDate(6, 9, -1); // 06-09-2022 processEntryDate(10, 11, 1993); // processEntryDate(10, 11, 1993); // 10-11-1993 } void processEntryDate(int day, int mon, int year) { if( year == -1) { std::time_t timer; time(&timer); // Artık 'epoch' tan geçen süreyi biliyoruz. // C-style // struct tm* ptr = localtime(&timer); // Approach I // tm* ptr = localtime(&timer); // Approach II : 'auto' anahtar sözcüğü yerine 'struct tm' türü gelecek. // auto* ptr = localtime(&timer); // Approach III : 'auto' anahtar sözcüğü yerine 'struct tm*' türü gelecek. auto ptr = localtime(&timer); year = ptr->tm_year + 1900 ; // Yıl bilgisini çekmiş olduk. // 'year' değişkeni olarak 'default argument' kullanıldığı için, bundan önceki değişken de // 'default argument' olabilir. if( mon == -1) { mon = ptr->tm_mon + 1; // 'mon' değişkeni olarak 'default argument' kullanıldığı için, bundan önceki değişken de // 'default argument' olabilir. if(day == -1) { day = ptr->tm_mday; } } } std::cout << std::setfill('0'); // Yukarıdaki 'std::setfill' isimli manipülatörü, tek kullanımlık değildir. Aksi olanlar standart akıma // geçilmediği müddetçe geçerlidir. std::cout << std::setw(2) << day << "-" << std::setw(2) << mon << "-" << year << "\n"; // Yukarıdaki 'std::setw()' isimli manipülatör ise tek kullanımlıktır. Sadece kendisinden bir sonraki // nesne için ayar çeker. // NOT : '\n' yerine 'std::endl' kullanılmamalı. Çünkü 'std::endl' ayrıca çıkış akımının buffer(ını) da // flush etmekte. Eğer bu flush işlemine gerek yokse, bu maliyete girilmemeli. }