> Standart Template Library (STL) : Başlangıç olarak 'std::string' sınıf şablonundan incelemeye başlayacağız. >> Esasında bu ('STL') kütüphanesinin amacı programcının rahat kod yazmasından ziyade yazılan kodun hızlı çalışmasını sağlamaktır. Dolayısla kullanıcı dostu değildir. >> 'std::string' sınıf şablonunun incelenmesi : Bir dinamik dizi sınıf şablonudur. Yani arkada dinamik bir dizi vardır. En çok kullanılan sınıf çeşididir. 'STL' kaplarından bir tanesidir. Temsili olarak şablon şu şekildedir, * Örnek 1, template, typename A = allocator> class basic_string{ }; // C : Yazının karakterinin türü // B : Yazının karakterleriyle ilgili temel işlemlerin nasıl yapılacağını belirleyen şablon parametresi. // Ekseriyet ile bu parametreye argüman geçmeyeceğiz, varsayılan argümanı kullanacağız. // A : Dinamik bellek yönetiminden sorumlu sınıfı belirlemektedir. Ekseriyet ile bu parametreye de argüman // geçmeyeceğiz, varsayılan argümanı kullanacağız. // 'string' ismi de bir 'typedef' tür eş isim bildirimi olduğundan, tanımlanmış şekli şudur, // typedef std::basic_string std::string; // diyebiliriz. int main() { std::basic_string myString; // LEGAL. // std::string myStringTwo; // LEGAL. std::basic_string myStringThree; // LEGAL. // wstring myStringFour; // LEGAL. } >>> 'Fat-Interface' sahiptir. Yani kullanıcıya sunduğu fonksiyonlar bir hayli fazladır. Bu fonksiyonların bazıları 'class-member-functions', bazıları 'global namespace functions' ve bazıları da 'algorithm' başlık dosyasında bildirilen/tanımlanan fonksiyon şablonlarından oluşmaktadır. >>> Başlık dosyamızın ismi 'string' şeklindedir. 'cstring' ismi ise C dilindeki 'string.h' başlık dosyasının C++ dilindeki hali. >>> 'string_view' başlık dosyasındaki fonksiyonlar ile 'regex' başlık dosyasındaki fonksiyonlar da bizim 'string' sınıfımız ile ilişkili fonksiyonlardır. >>> Bünyesinde üç adet değişken tutmaktadır. Bunlardan bir tanesi 'allocate' edilmiş bellek bloğunun başını, diğeri ise aynı bellek bloğunun sonunu tutmaktadır. Üçüncü gösterici ise bu bellek bloğundaki yazının sonunu göstermektedir. Ek olarak, ilgili yazının sonunu tutan ile bellek bloğunun sonunu tutan öğenin gösterici olma zorunluluğu da yok ama bellek bloğunun başını tutanın gösterici olma zorunluluğu vardır. Böylelikle 'pointer-aritmatich' mekanizmasını kullanarak da işlerimizi halledebiliriz. >>> İlgili sınıfımız arka planda dinamik dizi veri yapısında barındırmaktadır ki bu veri yapısında; >>>> Sondan yapılan ekleme ve silme işlemleri 'constant-time / O(1)' karmaşıklığındadır. Ama amorti edilmiş 'constant-time' olduğunu da unutmayalım. >>>> Sondan olmayan ekleme ve silme işlemleri ise 'linear-complexlity / O(n)' şeklinde. >>>> Yine aynı tip veri yapısında indeks ile erişim de 'constant-time' zaman karmaşıklığındadır. Çünkü döngüsel bir yapı kullanmamıza gerek kalmıyor. >>>> Ayrıca ilgili dinamik dizide yer kalmadığında, eğer yeniden bir ekleme işlemi daha yapılırsa, 'reallocation' süreci başlatılacak. Bu süreçte ise önce var olan bellek bloğunun 1.5 katı veya 2 katı büyüklüğünde yeni bir bellek alanı ayrılacak. Eski bellek alanındaki yazılar yeni bellek alanına TAŞINACAK. Sonrasında da eski bellek bloğu 'free' edilecek. Buradaki 'reallocation' senaryosuna dikkat etmemiz gerekiyor. Çünkü; DİKKATLİ KULLANILMADIĞINDA İLAVE BİR MALİYET GETİRMEKTEDİR. Örneğin, 40000 byte lık bir belleğimiz var ve ağzına kadar dolu. Biz ise sadece bir karakter eklemesi yapmamız gerekiyor. Bu durumda bu 40000 byte lık bellek alanımız taşınacaktır. Gereksiz yere 40000 byte lık bir alan taşınmış oldu. Bunun yerine bellek bloğunun kaç byte olacağını ÖN GÖREREK hareket etsek, işe başlamadan yeterli büyüklükte bir alanı bir defa da ayırsak, bu taşıma ve yeni yer israfına da gerek KALMAYABİLİR. Yani baştan 'reserve' yapmamız GÜZEL OLUR. Ek olarak bu 'reallocation' süreci 'string' sınıfımız içerisindeki 'pointer' ve 'reference' lar 'dangling' DURUMUNA DÜŞEBİLİRLER >>> [ 'size' / 'capacity' ] : [ Veri alanında o an tutulan toplam eleman sayısıdır / Veri alanında o an tutulabilecek maksimum eleman sayısıdır]. Örneğin, 'size' değerimiz 100 olsun ve 'capacity' değerimiz de 500 [100/500]. Buradan hareketle 'reallocation' olmadan 400 adet da eleman ekleyebiliriz. Bu durumda [ 500 / 500 ] olacaktır. Bundan sonraki yapılacak ilk ekleme işleminde 'reallocation' işlemi gerçekleşecektir. >>> 'std::string::size_type' : Bir tür eş ismidir. Genel olarak 'string' sınıfında 'size_t' türüne tekabül etmektedir, duruma göre başka bir tür de olabilir. Çünkü 'string' sınıfı da bir sınıf şablonu. İşte bu nedenden dolayı bizlerin 'string::size_type' şeklinde kullanması daha mantıklı. İş bu 'std::string:size_type' eş ismi aşağıdaki noktalarda kullanılmaktadır: >>>> 'string' sınıfının 'interface' sindeki fonksiyonların geri dönüş değer türü ve/veya parametre türü olarak kullanılmaktadır. >>>> 'string' sınıfındaki fonksiyonların bir çoğu yazıların karakterinin 'indeks' ini istemektedir. İşte bu indeksin türü 'std::string::size_type' türünden. >>>> 'string' sınıfındaki yazı uzunluğu türü olarak da 'std::string::size_type' kullanılmaktadır. >>>> Tane-adet türü olarak da 'std::string::size_type' kullanılmaktadır. >>> 'std::string' sınıfının 'interface' fonksiyonları: >>>> 'string::size_type string::size()const' fonksiyonu: Bir 'for-loop' içerisinde sürekli olarak bu fonksiyona çağrı yapılması sorun teşkil etmez. Derleyici 'inline' olarak açmaktadır iş bu fonksiyonu. Bir nevi sabit bir sayı kullanılmış gibi oluyor. Yazının uzunluk bilgisini döndürür. * Örnek 1, #include #include int main() { std::string name{"necati"}; for(size_t i = 0; i < name.size() ; ++i) { std::cout << "[" << name[i] << "]\n"; } } >>>> 'string::size_type string::length()const' fonksiyonu ve 'string::size_type string::capacity()const' fonksiyonu: * Örnek 1, #include #include int main() { /* # OUTPUT # --------- size : 9 leng : 9 capa : 15 --------- --------- size : 15 leng : 15 capa : 15 --------- --------- size : 16 leng : 16 capa : 30 --------- */ std::string str{"ali erkan"}; std::cout << "---------\n"; auto strSize = str.size(); // 'constant-time' operation // std::string::size_type n = str.size(); auto strLength = str.length(); // 'constant-time' operation // std::string::size_type n = str.length(); auto strCap = str.capacity(); // 'constant-time' operation // std::string::size_type n = str.capacity(); std::cout << "size : " << strSize << "\n"; std::cout << "leng : " << strLength << "\n"; std::cout << "capa : " << strCap << "\n"; std::cout << "---------\n"; str += "012345"; std::cout << "---------\n"; std::cout << "size : " << str.size() << "\n"; std::cout << "leng : " << str.length() << "\n"; std::cout << "capa : " << str.capacity() << "\n"; std::cout << "---------\n"; str += '6'; std::cout << "---------\n"; std::cout << "size : " << str.size() << "\n"; std::cout << "leng : " << str.length() << "\n"; std::cout << "capa : " << str.capacity() << "\n"; std::cout << "---------\n"; } * Örnek 2, Reserve işlemi gerçekleştirmemenin sonuçları: #include #include int main() { /* Link : https://godbolt.org/z/xnYGEPhfv # OUTPUT # - GCC & Clang 1. reallocation occured. Size / Capacity : [16 / 30] 2. reallocation occured. Size / Capacity : [31 / 60] 3. reallocation occured. Size / Capacity : [61 / 120] 4. reallocation occured. Size / Capacity : [121 / 240] 5. reallocation occured. Size / Capacity : [241 / 480] 6. reallocation occured. Size / Capacity : [481 / 960] 7. reallocation occured. Size / Capacity : [961 / 1920] 8. reallocation occured. Size / Capacity : [1921 / 3840] 9. reallocation occured. Size / Capacity : [3841 / 7680] 10. reallocation occured. Size / Capacity : [7681 / 15360] 11. reallocation occured. Size / Capacity : [15361 / 30720] 12. reallocation occured. Size / Capacity : [30721 / 61440] 13. reallocation occured. Size / Capacity : [61441 / 122880] 14. reallocation occured. Size / Capacity : [122881 / 245760] 15. reallocation occured. Size / Capacity : [245761 / 491520] 16. reallocation occured. Size / Capacity : [491521 / 983040] 17. reallocation occured. Size / Capacity : [983041 / 1966080] # OUTPUT # - MSVC 1. reallocation occured. Size / Capacity : [16 / 31] 2. reallocation occured. Size / Capacity : [32 / 47] 3. reallocation occured. Size / Capacity : [48 / 70] 4. reallocation occured. Size / Capacity : [71 / 105] 5. reallocation occured. Size / Capacity : [106 / 157] 6. reallocation occured. Size / Capacity : [158 / 235] 7. reallocation occured. Size / Capacity : [236 / 352] 8. reallocation occured. Size / Capacity : [353 / 528] 9. reallocation occured. Size / Capacity : [529 / 792] 10. reallocation occured. Size / Capacity : [793 / 1188] 11. reallocation occured. Size / Capacity : [1189 / 1782] 12. reallocation occured. Size / Capacity : [1783 / 2673] 13. reallocation occured. Size / Capacity : [2674 / 4009] 14. reallocation occured. Size / Capacity : [4010 / 6013] 15. reallocation occured. Size / Capacity : [6014 / 9019] 16. reallocation occured. Size / Capacity : [9020 / 13528] 17. reallocation occured. Size / Capacity : [13529 / 20292] 18. reallocation occured. Size / Capacity : [20293 / 30438] 19. reallocation occured. Size / Capacity : [30439 / 45657] 20. reallocation occured. Size / Capacity : [45658 / 68485] 21. reallocation occured. Size / Capacity : [68486 / 102727] 22. reallocation occured. Size / Capacity : [102728 / 154090] 23. reallocation occured. Size / Capacity : [154091 / 231135] 24. reallocation occured. Size / Capacity : [231136 / 346702] 25. reallocation occured. Size / Capacity : [346703 / 520053] 26. reallocation occured. Size / Capacity : [520054 / 780079] 27. reallocation occured. Size / Capacity : [780080 / 1170118] */ std::string str{"necati ergin"}; auto lastCap = str.capacity(); int reallocationCounter = 0; for(int i = 0; i < 1000000; ++i) { str.push_back('x'); if(str.capacity() > lastCap) { std::cout << ++reallocationCounter << ". reallocation occured. Size / Capacity : [" << str.size() << " / " << str.capacity() << "]\n"; lastCap = str.capacity(); } } } * Örnek 3, Reserve işlemi gerçekleştirmenin sonuçları: #include #include int main() { /* Link : https://godbolt.org/z/3x9sevT7q # OUTPUT # - GCC & Clang 1. reallocation occured. Size / Capacity : [500001 / 1000000] 2. reallocation occured. Size / Capacity : [1000001 / 2000000] # OUTPUT # - MSVC 1. reallocation occured. Size / Capacity : [500016 / 750022] 2. reallocation occured. Size / Capacity : [750023 / 1125033] */ std::string str{"necati ergin"}; str.reserve(500000); // Yazının uzunluğunun bir şekilde 500.000 karakter olduğu ön görülmüş olsun. auto lastCap = str.capacity(); int reallocationCounter = 0; for(int i = 0; i < 1000000; ++i) { str.push_back('x'); if(str.capacity() > lastCap) { std::cout << ++reallocationCounter << ". reallocation occured. Size / Capacity : [" << str.size() << " / " << str.capacity() << "]\n"; lastCap = str.capacity(); } } } >>>> Parametrik yapısı 'const std::string&' şeklinde olan fonksiyonlar, bizden bir 'string' sınıf türünden nesne istemektedir. >>>> Parametrik yapısı 'const char*' şeklinde olan fonksiyonlar ise bizden 'Null-terminated-string / C-string' istemektedir. Bu fonksiyona geçilecek yazının sonunda '\0' karakterinin olması kullanıcı olarak bizlerin sorumluluğunda. Eğer sonunda '\0' karakteri olmayan bir adres/yazı geçersek de 'Tanımsız Davranış' meydana gelir. Herhangi bir 'exception throw' ETMEMEKTEDİR. >>>> Parametrik yapısı 'const char*, size_t' şeklinde olan fonksiyonlara kabaca 'data' parametreli fonksiyonlar denmektedir. Bu da şu manaya gelmektedir: BİZDEN BİR ADRES İSTENMEKTEDİR VE BU ADRESTEN SONRA KAÇ KARAKTER ÜZERİNDE İŞLEM YAPILACAKSA O KARAKTERİN ADEDİ İSTENMEKTEDİR. Örneğin, 'str, 10' şeklinde bir argüman geçildiğide 'str' adresinden itibaren ilk 10 karakterlik yazı argüman oluyor. Velevki 'str' adresindeki dizinin uzunluğu 10 karakterden de az ise, 'Tanımsız Davranış' meydana gelecektir. Haliyle geçilecek olan argümanın taşma yapıp yapmayacağı bizim sorumluluğumuzda. Bu konunun 'Null-Character' ile bir alakası YOKTUR. >>>> Parametrik yapısı 'const char*, const char*' şeklinde olan fonksiyonlara 'range' parametreli fonksiyonlar denmektedir. Burada sol taraftaki argüman olan ifadenin 'range' kapsamında olduğu, sağ taraftaki argümanın ise 'range' kapsamında olmadığını belirtmeliyiz. Yani bir tanesi başlangıç noktası, diğeri ise bitiş noktası. Fakat sol taraftaki ifade eğer sağ taraftaki ifadeye eşit olamayacaksa yine 'Tanımsız Davranış' meydana gelir. Örneğin, sol taraftaki adresin çifter çifter arttırılması sonucu, sağ taraftaki adresi es geçmesi. Bu iki konum bilgisi arasındaki yazı argüman olarak geçilmektedir. >>>> Parametrik yapısı 'size_t, char' türden olan fonksiyonlara 'fill' parametreli fonksiyonlar denmektedir. Yani argüman olarak 'n' adedince 'c' karakteri. >>>> Parametrik yapısı 'const std::string&, size_t' türden olan fonksiyonlara 'sub-string' parametreli fonksiyonlar denmektedir ve geçilen argümanlardan sol taraftaki 'string' nesnesinin sağ taraftaki argüman olan indeksten başlayarak işlem yapmaktadır. >>>> Parametrik yapısı 'const std::string&, size_t, size_t' türden olan fonksiyonlara özelleştirilmiş 'sub-string' fonksiyonlar denmektedir. Bunlar sol argüman olan sınıf nesnesinin ortadaki argüman olan indeksinden başlayarak sağ argüman olan karakter adedince ilerleyen işlemler yapmaktadır. >>>> Parametrik yapısı 'char' olanlar ise sadece bir karakter istemektedir. >>>> Parametrik yapısı 'initializer_list' olan fonksiyonlar vardır. Bunlar '{}' içerisine virgüller ile ayrılan karakterler 'initializer_list' oluşturur. Örneğin, "{'a', 'l', 'p'}" şeklindeki argümanlardır. >>> 'string' sınıfımız bir sınıf şablonu olduğundan, sadece çağırmış olduğumuz fonksiyonların kodları yazılacaktır. * Örnek 1, //.. int main() { // 'Ctor' kodu yazıldı. 20 elemanlı ve elemanları 'null-string' olan bir yazı oluşturuldu. std::string str(20, '0'); str.length(); // İş bu fonksiyonun kodu şimdi yazıldı. // 'Dtor' çağrılacağı için onun da kodu yazıldı. } >>> 'string' sınıfının Kurucu İşlevlerinin incelenmesi: >>>> 'string' nesnesinin 'Default Init.' edilmesi : Uzunluğu sıfır olan, herhangi bir yazı da tutmayan bir nesnedir. * Örnek 1, #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { std::string s1; print(s1); // OUTPUT => [0] = "" } >>>> 'C-string' Parametreli 'Ctor.' : 'Null-Karakter' görene kadar geçilen karakterlerden bir yazı oluşturulacak. * Örnek 1, #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { std::string s2 = "alican"; // std::string s2("alican"); // 'direct init.' // std::string s2{"alican"}; // 'direct-list init.' print(s2); // OUTPUT => [6] = "alican" char buffer[] = "today is saturday."; std::string s3{buffer}; print(s3); // OUTPUT => [18] = "today is saturday." } >>>> 'Range' Parametreli 'Ctor' : İlk argümanın gösterdiği karakter dahil, ikinci argümanın gösterdiği karakter hariç, bu iki argüman arasındaki yazıların tamamı ile oluşturulan yazı. * Örnek 1, #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { char buffer[] = "today is saturday."; std::string s3{buffer, buffer + 5}; print(s3); // OUTPUT => [5] = "today" std::string s2{buffer + 6, buffer + 8}; print(s2); // OUTPUT => [2] = "is" } >>>> 'Data' Parametreli 'Ctor' : İlk argüman olarak bir adres, ikinci argüman olarak da bir tam sayı alan ve yazıyı ilgili adresten başlayıp ilgili tam sayı kadar gittikten sonra oluşturulan yazı. 'Range' parametre ile KARIŞTIRMAYALIM. * Örnek 1, //.. #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { char buffer[] = "today is saturday."; std::string s3{buffer, 8}; print(s3); // OUTPUT => [8] = "today is" std::string s2{buffer + 8, 9}; print(s2); // OUTPUT => [9] = " saturday" std::string s1{buffer, 60}; // 'Tanımsız Davranış' print(s1); // OUTPUT => [60] = "today is saturday.w[htIZp��bd�o�+eU��;" } >>>> 'Init. List' Parametreli 'Ctor' : Virgüller ile ayrılmış karakterlerden yazı meydana getirilmesi. Bu listenin elemanları 'char' türden yada 'char' türüne dönüştürülebilecek bir tür olmak ZORUNDA. * Örnek 1, #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { std::string s1 = {'e', 'n', 'e', 's'}; // std::string s2('e', 'n', 'e', 's'); // 'direct init.' // SENTAKS HATASI std::string s3{'e', 'n', 'e', 's'}; // 'direct-list init.' print(s1); // OUTPUT => [4] = "enes" print(s3); // OUTPUT => [4] = "enes" } >>>> 'fill' Parametreli 'Ctor' : Birinci argümanı 'size_t' türünden, ikinci argüman ise bir karakter sabiti olan ve 'n' adedince 'c' karakteri ile doldurulan bir yazı. * Örnek 1, #include #include void print(const std::string& other) { std::cout << "[" << other.length() << "] = \"" << other << "\"\n"; } int main() { std::string s1(3, 'a'); // 'direct init.' OLDUĞUNA DİKKAT EDİN. print(s1); // OUTPUT => [3] = "aaa" } >>> 'Fill' parametreli 'Ctor' ile 'initializer_list' parametreli 'Ctor' karşılaştırması: '{}' çifti ile nesne hayata getirdiğimiz zaman 'initializer_list' parametreli 'Ctor' çağrılacaktır. * Örnek 1, #include #include int main() { std::string str(49, 'A'); // 12 adet 'A' karakteri ile bir yazı oluşturuyor. std::cout << "[ " << str << " ]\n"; // OUTPUT => [ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ] std::string strstr{49, 'A'}; // ilgili 'Ctor' fonksiyonun parametresi 'initializer_list' şeklinde.Bundan dolayı her iki argüman // da Karakter Sabiti olarak ele alınacaktır. ASCII tablosuna göre 49 rakamının karakter karşılığı '1' // olduğundan '1A' yazısı oluşturuldu. std::cout << "[ " << strstr << " ]\n"; // OUTPUT => [ 1A ] } >>> Bir 'RAII' sınıfı olduğundan, 'string' sınıfının da bir 'Copy Ctor' ve 'Move Ctor' u var: * Örnek 1, #include #include int main() { std::string str{"necati ergin"}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [12] : necati ergin std::string strTwo = std::move(str); // Move Ctor çağrıldı. std::cout << "[" << strTwo.size() << "] : " << strTwo << "\n"; // OUTPUT => [12] : necati ergin std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : std::string strThree(strTwo, 6); // 'strTwo' adresinden itibaren altı karakter ileri git ve o karakterden itibaren bir yazı oluştur. std::cout << "[" << strThree.size() << "] : [" << strThree << "]\n"; // OUTPUT => [6] : [ ergin] std::string strFour(strTwo, 6, 3); // 'strTwo' adresinden itibaren altı karakter ileri git ve o karakterden ile o karakterden üç karakter // sonraki karakter arasındaki karakterler ile bir yazı oluştur. std::cout << "[" << strFour.size() << "] : [" << strFour << "]\n"; // OUTPUT => [3] : [ er] } >>> 'string' sınıfında 'char' parametreli bir 'Ctor' YOKTUR. Amacımız tek bir karakter ile birden fazla yöntem mevcut. * Örnek 1, #include #include int main() { // std::string str('A'); // error: no matching function for call to ‘std::__cxx11::basic_string::basic_string(char)’ // C-String Parametreli Ctor. Çağrısı: std::string str("A"); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [1] : [A] // Fill Parametreli Ctor. Çağrısı: std::string strTwo(1, 'A'); std::cout << "[" << strTwo.size() << "] : [" << strTwo << "]\n"; // OUTPUT => [1] : [A] // Init. List Ctor. Çağrısı: std::string strThree{'A'}; std::cout << "[" << strThree.size() << "] : [" << strThree << "]\n"; // OUTPUT => [1] : [A] } >>> Amacımız bir yazı oluşturmak ve o yazıyı döndürmek ise 'Call-by-value' mekanizmasını kullanmamız tavsiye edilmekte. Çünkü gerek 'Move Semantics' gerek 'Copy Ellision' mekanizmalarından dolayı kopyalama elimine ediliyor. * Örnek 1, #include #include class Myclass{ public: Myclass() { std::cout << "Myclass() was called. Address : " << this << "\n"; } Myclass(Myclass&& other) { std::cout << "Myclass(Myclass&& other) was called. Adress : " << this << "\n"; } }; Myclass func() { Myclass m; // I std::cout << "&m : " << &m << "\n"; // II return m; } int main() { /* # OUTPUT # I => Myclass() was called. Address : 0x7fffe0c5a957 II => &m : 0x7fffe0c5a957 III => &mm : 0x7fffe0c5a957 */ Myclass mm = func(); std::cout << "&mm : " << &mm << "\n"; // III } >>> 'string' sınıfının 'member-functions' incelemesi : >>>> 'string' sınıfının 'operator<<' fonksiyonu ve 'operator>>' fonksiyonları: Boşluk karakterleri yazıya dahil edilmemektedir. Onlar tıpkı 'scanf()' fonksiyonunda olduğu gibi ayraç olarak kullanılmakta. O yazılar buffer da kalıyor. Bu durumda bütün karakterleri buffer dan almak istiyorsak 'getline()' fonksiyonunu kullanmalıyız. C dilinde ise '.gets()/.gets_s()' ve '.fgets()' fonksiyonları yapmaktadır. * Örnek 1, #include #include int main() { std::string str, strTwo, strThree, strFour, strFive, strSix; std::cout << "Bir yazi girin : " ; std::cin >> str; // INPUT => c++ dili ne kadar da guzel degil mi? bence oyle. Hele de ogretmenın de kaliteli ise. std::cin >> strTwo; std::cin >> strThree; std::cin >> strFour; std::cin >> strFive; std::cin >> strSix; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [3] : c++ std::cout << "[" << strTwo.size() << "] : " << strTwo << "\n"; // OUTPUT => [4] : dili std::cout << "[" << strThree.size() << "] : " << strThree << "\n"; // OUTPUT => [2] : ne std::cout << "[" << strFour.size() << "] : " << strFour << "\n"; // OUTPUT => [5] : kadar std::cout << "[" << strFive.size() << "] : " << strFive << "\n"; // OUTPUT => [2] : da std::cout << "[" << strSix.size() << "] : " << strSix << "\n"; // OUTPUT => [5] : guzel std::string strSeven; std::getline(std::cin, strSeven); std::cout << "[" << strSeven.size() << "] : " << strSeven << "\n"; // OUTPUT => [59] : degil mi? bence oyle. Hele de ogretmenın de kaliteli ise. } >>>> Yazının karakterlerine erişim için yazılan fonksiyonların ayrıca 'const-overload' versiyonları da vardır. >>>>> '.operator[]' : 'const' olmayan nesnelerin ilgili indeksteki karakterine okuma ve yazma amacı ile erişebiliriz. Çünkü geri dönüş değeri 'call-by-reference' şeklinde. 'const' olan sınıf nesneler için bu operatorün geri dönüş değeri 'const-call-by-reference' olduğundan, sadece okuma amacı ile çağırabiliriz. Geçersiz indeks bilgisi geçildiğinde 'Tanımsız Davranış' meydana gelir, herhangi bir 'exception throw' ETMEZ. Yine bu operatörü kullansam, adresini bir C API sine gönderebilirim. * Örnek 1, #include #include void func(const char* ptr, size_t len); int main() { std::string name{"mehmet aksu"}; for(size_t i = 0; i < name.size(); ++i) std::cout << "[" << name[i] << "] "; // OUTPUT => [m] [e] [h] [m] [e] [t] [ ] [a] [k] [s] [u] std::cout << "\n"; name[8] = 'L'; std::cout << "[" << name.size() << "] : " << name << "\n"; // OUTPUT => [11] : mehmet aLsu const std::string surname{"cantoprak"}; // surname[2] = 'P'; // İlgili '.operator[]' fonksiyonunun 'const-overload' çağrılacağı için artık YAZMA YAPAMIYORUZ. try{ auto c = name[45]; // 'Tanımsız Davranış'. Ayrıca 'exception-throw' ETMEMEKTEDİR. } catch(std::exception& ex) { std::cout << "exception yakalandi : " << ex.what() << "\n"; // OUTPUT => } func(&name[0], name.size()); // LEGAL } >>>>> '.at()' fonksiyonu : Yukarıdaki '.operator[]' fonksiyonunda yazılanlar bu fonksiyon için de geçerlidir. Ekstra olarak bu fonksiyon hata durumunda 'exception-throw' etmektedir. * Örnek 1, #include #include int main() { std::string name{"mehmet aksu"}; for(size_t i = 0; i < name.size(); ++i) std::cout << "[" << name.at(i) << "] "; // OUTPUT => [m] [e] [h] [m] [e] [t] [ ] [a] [k] [s] [u] std::cout << "\n"; name.at(8) = 'L'; std::cout << "[" << name.size() << "] : " << name << "\n"; // OUTPUT => [11] : mehmet aLsu const std::string surname{"cantoprak"}; // surname.at(5) = 'P'; // İlgili '.at()' fonksiyonunun 'const-overload' çağrılacağı için artık YAZMA YAPAMIYORUZ. try{ auto c = name.at(45); // 'Tanımsız Davranış'. Ayrıca 'exception-throw' ETMEMEKTEDİR. } catch(std::exception& ex) { std::cout << "exception yakalandi : " << ex.what() << "\n"; // OUTPUT => exception yakalandi : // basic_string::at: __n (which is 45) >= this->size() (which is 11) } } >>>>> '.front()' ve '.back()' fonksiyonları: Sırasıyla yazının ilk ve son karakterlerine erişim için kullanılır. Yine aynı şekilde 'const' nesneler için bu fonksiyonların da 'const-overload' ı vardır. Ayrıca ilgili 'string' nesnesi boşsa bu iki fonksiyon 'Tanımsız Davranış' a neden olur. * Örnek 1, #include #include int main() { std::string name{"beyhan"}; ++name.front(); std::cout << "[" << name.size() << "] : " << name << "\n"; // OUTPUT => [6] : ceyhan name.back() = '*'; std::cout << "[" << name.size() << "] : " << name << "\n"; // OUTPUT => [6] : ceyha* const std::string surname{"aksu"}; // İlgili '.front()' ve '.back()' fonksiyonlarının 'const-overload' çağrılacağı için artık // YAZMA YAPAMIYORUZ. // ++surname.front(); // surname.back() = '*'; } >>>> 'string' sınıfındaki yazıyı dolaşma yolları: 'iterator' mekanizması, '[]' operatörü ile ve '.at()' fonksiyonu ile yapabiliriz. Bu üçü ile OKUMA ve YAZMA yapabiliriz. Bu üç kalemin de ayrıca 'const-overload' versiyonu da vardır ki onlar ile sadece OKUMA yapabiliriz. * Örnek 1, #include #include int main() { std::string str{"Berat Albayrak"}; for(size_t i = 0; i < str.size(); ++i) { std::cout << "[" << str[i] << "] "; // OUTPUT => [B] [e] [r] [a] [t] [ ] [A] [l] [b] [a] [y] [r] [a] [k] // std::cout << "[" << str.at(i) << "] "; } std::cout << "\n"; for(auto iter = str.begin(); iter != str.end(); ++iter) std::cout << "[" << *iter << "] "; // OUTPUT => [B] [e] [r] [a] [t] [ ] [A] [l] [b] [a] [y] [r] [a] [k] std::cout << "\n"; //for(auto c : str) // 'for' döngüsünü derleyici yazmaktadır. 'call-by-value' mekanizması kullanıldığından, ilgili // 'string' nesnesindeki her bir karakter 'c' değişkenine kopyalanmaktadır. 'c' karakteri üzerindeki // değişiklik ilgili 'str' nesnesindeki yazıyı değiştirmeyecektir. for(char c : str) std::cout << "[" << c << "] "; // OUTPUT => [B] [e] [r] [a] [t] [ ] [A] [l] [b] [a] [y] [r] [a] [k] //for(auto& c : str) // 'for' döngüsünü derleyici yazmaktadır. 'call-by-reference' mekanizması kullanıldığından, ilgili // 'string' nesnesindeki her bir karakter 'c' değişkenine refere edilmektedir. 'c' karakteri // üzerindeki değişiklik ilgili 'str' nesnesindeki yazıyı değiştirecektir. for(char& c : str) std::cout << "[" << c << "] "; // OUTPUT => [B] [e] [r] [a] [t] [ ] [A] [l] [b] [a] [y] [r] [a] [k] //for(const auto& c : str) // 'for' döngüsünü derleyici yazmaktadır. 'const-call-by-reference' mekanizması kullanıldığından, // ilgili 'string' nesnesindeki her bir karakter 'c' değişkenine refere edilmektedir. İlgili 'c' // karakteri üzerinde DEĞİŞİKLİK YAPILAMAZ. for(const char& c : str) std::cout << "[" << c << "] "; // OUTPUT => [B] [e] [r] [a] [t] [ ] [A] [l] [b] [a] [y] [r] [a] [k] } >>>> Arka plandaki kahraman fonksiyonların incelenmesi; >>>>> '.reserve()' fonksiyonu : Kapasiteyi rezerve etmektedir. 'size' büyüklüğü değişmiyor. 'capacity' değeri de bu rakama yaklaşık olmaktadır. 'size' büyüklüğü, 'capacity' değerini aşarsa 'reallocation' gerçekleşecektir. Bu fonksiyonu kullanarak 'capacity' değerini küçültemeyiz. Bunu yapmaya çalıştığımız zaman 'non-binding shrink request' mevzusu olur. Sentaks hatası değildir, 'Tanımsız Davranış' değildir. Derleyici 'capacity' değerini KÜÇÜLTMEK ZORUNDA DEĞİLDİR. Varsayılan argüman olarak da '0' değerini alır. * Örnek 1, #include #include int main() { std::string str; str.reserve(100); // GCC & CLANG || MSVC std::cout << "str : " << str << "\n"; // OUTPUT => str : || std::cout << "Size : " << str.size() << "\n"; // OUTPUT => Size : 0 || 0 std::cout << "Capacity : " << str.capacity() << "\n"; // OUTPUT => Capacity : 100 || 111 str.reserve(50); std::cout << "str : " << str << "\n"; // OUTPUT => str : || std::cout << "Size : " << str.size() << "\n"; // OUTPUT => Size : 0 || 0 std::cout << "Capacity : " << str.capacity() << "\n"; // OUTPUT => Capacity : 50 || 111 str.reserve(); // Varsayılan argüman olarak '0' değeri geçildi. std::cout << "str : " << str << "\n"; // OUTPUT => str : || std::cout << "Size : " << str.size() << "\n"; // OUTPUT => Size : 0 || 0 std::cout << "Capacity : " << str.capacity() << "\n"; // OUTPUT => Capacity : 15 || 15 } >>>>> '.shrink_to_fit()' fonksiyonu : 'capacity' değerini, 'size' büyüklüğüne uygun olacak şekilde büzer. Herhangi bir argüman almamaktadır. C++11 ile dile eklenmiştir. * Örnek 1, #include #include int main() { std::string str; str = "ahmet kandemir pehlivanli"; std::cout << "str : " << str << "\n"; // OUTPUT => str : ahmet kandemir pehlivanli std::cout << "Size : " << str.size() << "\n"; // OUTPUT => Size : 25 std::cout << "Capacity : " << str.capacity() << "\n"; // OUTPUT => Capacity : 30 str.shrink_to_fit(); std::cout << "str : " << str << "\n"; // OUTPUT => str : ahmet kandemir pehlivanli std::cout << "Size : " << str.size() << "\n"; // OUTPUT => Size : 25 std::cout << "Capacity : " << str.capacity() << "\n"; // OUTPUT => Capacity : 25 } >>>>> '.c_str()' fonksiyonu : 'string' sınıf türünden 'const char*' türüne otomatik dönüşüm YOKTUR. Ama 'C-string' isteyen bir fonksiyonumuz var elimizde. '.c_str()' fonksiyonunu kullanarak iş bu fonksiyona çağrı yapabiliriz. Bu fonksiyonun geri dönüş değeri 'const char*' türündendir. 'reallocation' sonrasında bu fonksiyonu tekrardan çağırmamız gerekiyor, içerideki göstericinin gösterdiği adres değiştiğinden dolayı. * Örnek 1, #include #include #include int main() { std::string name{"enes aydin"}; auto ptr = name.c_str(); std::cout << "using ptr : " << ptr << "\n"; // OUTPUT => using ptr : enes aydin std::cout << "using C-Api, length : " << std::strlen(name.c_str()) << "\n"; // OUTPUT => using C-Api, length : 10 std::cout << "&name[0], length : " << std::strlen(&name[0]) << "\n"; // OUTPUT => &name[0], length : 10 name += "isimli kisinin ismi orneklerde sikca gecmektedir."; std::cout << "using ptr : " << ptr << "\n"; // OUTPUT => using ptr : ; // 'ptr' is dangling pointer now. Çünkü 'reallocation' gerçekleşti. // Eğer gerçekleşmeseydi, 'ptr' göstericisini hala kullanabiliriz. // OUTPUT => using C-Api, length : 59 std::cout << "using C-Api, length : " << std::strlen(name.c_str()) << "\n"; // &name[0], length : 59 std::cout << "&name[0], length : " << std::strlen(&name[0]) << "\n"; // Görüldüğü üzere '.c_str()' fonksiyonunu her zaman tekrar tekrar çağırmamız gerekmektedir. } >>>>> '.data()' fonksiyonu : C++17 ile bu üye fonksiyonun 'global namespace' versiyonu 'algorithm' başlık dosyasına eklendi. Her iki versiyon da yukarıdaki '.c_str()' fonksiyonu ile aynı işi yapmaktadır. Modern C++ sonrasındaki dönemde ikisi arasında bir fark YOKTUR. O dönem öncesinde '.data()' ile çağrılan fonksiyonun 'Null-terminated-string' olma ZORUNLULUĞU YOKTU. >>>>> '.copy' fonksiyonu : Az çağrılan bir fonksiyondur. İlk argüman, kopyalanacak adres. İkinci argüman kopyalanacak toplam karakter adedi. Üçüncü argüman ise off-set. O off-set değerinden itibaren kopyalama başlayacak. Geri dönüş değeri de kopyalanan karakter sayısıdır. Kopyalanacak karakter sayısı, hedef dizindeki eleman sayısından büyük ise, kopyalama işleminden sonra '\0' karakterini eklemeliyiz çünkü kopyalama sırasında '\0' karakterini KOPYALANMAMAKTADIR. * Örnek 1, #include #include int main() { std::string name{"necati ergin bugun ders anlatti."}; char str[10]; // 'name' nesnesinin içindeki yazının yedinci karakterinden itibaren toplam on karakter // kopyalanacaktır. size_t n = name.copy(str, 10, 7); std::cout << "[" << n << "] : [" << str << "]\n"; // OUTPUT => [10] : [ergin bugu] std::string surname{"necati"}; char strTwo[10]; size_t m = surname.copy(strTwo, 10); strTwo[m] = '\0'; // İlgili karakter kopyalanmadığından, bizim eklememiz gerekiyor. std::cout << "[" << m << "] : [" << strTwo << "]\n"; // OUTPUT => [6] : [necati] } >>>> 'string' sınıfındaki arama fonksiyonları : Bütün arama fonksiyonlarının ara yüzü ortaktık. Geri dönüş değerinin türü 'std::string::size_type' şeklindedir. Başarısız olma durumunda 'std::string::npos' değerini döndürmektedir. Birinci parametre HER ZAMAN ARANAN DEĞERDİR. >>>>> 'constexpr std::string::size_type std::string::npos' : ÇOK ÖNEMLİ BİR 'static' VERİ ELEMANIDIR. Türü, 'std::string::size_type' şeklindedir. Değeri bu türün en büyük değeri. 'std::string' sınıfında bu tür 'size_t' şeklindedir. 'string' sınıfındaki arama fonksiyonlarının ki bunlar '.find()', '.rfind()', '.find_first_of()', '.find_last_of()', '.find_first_not_of()', '.find_last_not_of()' fonksiyonlarıdır, başarısız olmaları durumunda geri dönüş değeri olarak kullanılır. İş bu fonksiyonlar başarılı olmaları durumunda ise 'std::string::size_type' türünden değer döndürmektedirler. Bu sabitin bir diğer kullanım alanı ise şurasıdır; bazı fonksiyonlar bizden bir indeks ve toplam karakter sayısı istemektedir. Böylelik o indeksten başlayıp, o kadar karakter sayısı kadarlık yazı üzerinde işlem yapabilsinler. Eğer bir şekilde argüman olarak geçeceğimiz rakam, dizinin toplam uzunluğundan büyükse, ne SENTAKS HATASI NE DE 'Tanımsız Davranış' meydana geliyor. Bu durumda yazının o indisten itibaren geri kalan bütün hepsi ele alınıyor. Peki ya yazının toplam uzunluğunu bilmiyorsak? İşte bu tip durumlarda %100 güvenli yöntem olarak 'std::string::npos' değerini argüman olarak geçmektir. Böylelikle o indisten itibaren yazının geri kalanı ele alınmış oluyor. DİPNOT: C dilindeki arama fonksiyonları ise adres döndürmektedir. * Örnek 1, #include #include int main() { std::cout << "std::string::npos : " << std::string::npos << "\n"; // OUTPUT => std::string::npos : 18446744073709551615 std::cout << "(size_t)(-1) : " << (size_t)(-1) << "\n"; // OUTPUT => std::string::npos : 18446744073709551615 std::string str; std::cout << "Bir yazi giriniz: "; std::getline(std::cin, str); // INPUT => Ahmet Kandemir Pehlivanli auto idx = str.find('a'); if(idx != std::string::npos) { std::cout << "bulundu. indeks : " << idx << "\n"; // OUTPUT => bulundu. indeks : 7 str[idx] = '@'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [25] : Ahmet K@ndemir Pehlivanli } else { std::cout << "bulunamadi."; } } >>>>> '.find()' fonksiyonu : Birinci parametre aranacak öğe, ki bu öğe bir 'C-String' olabilir, 'string'/'sub-string' olabilir, 'Data' Parametre olabilir. Birden çok 'overload' mevcut. İkinci parametre ise off-set. Yani kaçıncı indeksten aramaya başlanacağını söylüyor. Varsayılan argüman ise sıfırıncı indeks. Bulduğu ilk indekte arama sonlanır. Arama baştan sonra doğru yapılır. * Örnek 1, 'e' karakterinin aranması. #include #include int main() { std::string str{"Bjarne Stroustrup invented C++."}; if(auto idx = str.find('e'); idx != std::string::npos) { std::cout << "Bulundu. Indeks : " << idx << "\n"; // OUTPUT => Bulundu. Indeks : 5 str[idx] = '!'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [31] : Bjarn! Stroustrup invented C++. } else { std::cout << "Bulunamadı...\n"; } } * Örnek 2, 'e' karakterinin belirli bir indeksten itibaren aranması. #include #include int main() { std::string str{"Bjarne Stroustrup invented C++."}; if(auto idx = str.find('e', 6); idx != std::string::npos) { std::cout << "Bulundu. Indeks : " << idx << "\n"; // OUTPUT => Bulundu. Indeks : 21 str[idx] = '!'; // OUTPUT => [31] : Bjarne Stroustrup inv!nted C++. std::cout << "[" << str.size() << "] : " << str << "\n"; } else { std::cout << "Bulunamadı...\n"; } } * Örnek 3, Bir 'C-String' aranması. #include #include int main() { std::string str{"Bjarne Stroustrup invented C++."}; // "up" kelimesinin başladığı indeksi bize döndürecektir. Eğer böyle bir kelime olmasaydı, // ifade 'else' bloğuna girecektir. if(auto idx = str.find("up"); idx != std::string::npos) { std::cout << "Bulundu. Indeks : " << idx << "\n"; // OUTPUT => Bulundu. Indeks : 15 str[idx] = '!'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [31] : Bjarne Stroustr!p invented C++. } else { std::cout << "Bulunamadı...\n"; } } * Örnek 4, Bir yazı içerisindeki bütün 'e' karakterinin bulunması. #include #include int main() { std::string str{"Bjarne Stroustrup invented C++."}; for(size_t idx = 0; idx < str.size(); ++idx) { if(size_t lastFoundIndex = str.find('e'); lastFoundIndex != std::string::npos) { lastFoundIndex = str.find('e', lastFoundIndex); // En son bulduğu karakterden aramaya başlıyor. str[lastFoundIndex] = '!'; } } std::cout << "[" << str.size() << "] : " << str << "\n"; } >>>>> '.rfind()' fonksiyonu : Bir yazıda aramaya sondan başlıyor. '.find()' fonksiyonunun 'reverse' edilmiş hali diyebiliriz. Bunun haricinde '.find()' fonksiyonundan bir farkı yoktur. Bulduğu ilk indekste aramayı sonlandırır. >>>>> '.find_first_of()' : İlk parametre yine her zaman olduğu gibi aranan değer. İkinci olarak ise aramaya başlanacak indeks için off-set. 'C-String' bir ifade argüman olarak geçildiğinde, kelimenin harflerini ayrı ayrı arayacaktır. '.find()' fonksiyonu gibi kelimeyi bütün halde aramayacaktır. * Örnek 1, #include #include int main() { std::string str; std::cin >> str; // INPUT => trxmoprnwertqs std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxmoprnwertqs // "aeiou" kelimesindeki harflerden birisinin ilk bulunduğu indeksi bize döndürecektir. // Kelimenin bütününü aramamaktadır. if(auto idx = str.find_first_of("aeiou"); idx != std::string::npos) { std::cout << "Bulundu. idx : " << idx << "\n"; // OUTPUT => Bulundu. idx : 4 str[idx] = '*'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxm*prnwertqs } else { std::cout << "Bulunamadı.\n"; } } >>>>> '.find_last_of()' fonksiyonu : '.find_first_of()' fonksiyonu gibi çalışmaktadır. Tek fark, bu fonksiyon aramaya sondan başlayacaktır. Ayrıca döndürdüğü indeks numarası, baştan başladığımız zamanki indeks numarasıdır. * Örnek 1, #include #include int main() { std::string str{"trxmoprnwertqs"}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxmoprnwertqs if(auto idx = str.find_last_of("aeiou"); idx != std::string::npos) { std::cout << "Bulundu. idx : " << idx << "\n"; // OUTPUT => Bulundu. idx : 9 str[idx] = '*'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxmoprnw*rtqs } else { std::cout << "Bulunamadı.\n"; } } >>>>> '.find_first_not_of()' fonksiyonu : Argüman olarak geçilen ifadedeki karakterlerden olmayan ilk karakteri bulacaktır. * Örnek 1, #include #include int main() { std::string str{"trxmoprnwertqs"}; // OUTPUT => [14] : trxmoprnwertqs std::cout << "[" << str.size() << "] : " << str << "\n"; //Bu karakterlerden biri olmayan ilk karakteri bulacaktır. if(auto idx = str.find_first_not_of("aeiou"); idx != std::string::npos) { std::cout << "Bulundu. idx : " << idx << "\n"; // OUTPUT => Bulundu. idx : 0 str[idx] = '*'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : *rxmoprnwertqs } else { std::cout << "Bulunamadı.\n"; } } >>>>> '.find_last_not_of()' fonksiyonu : Argüman olarak geçilen ifadedeki karakterlerden olmayan ilk karakteri bulacaktır fakat aramaya sondan başlayacaktır. * Örnek 1, #include #include int main() { std::string str{"trxmoprnwertqs"}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxmoprnwertqs //Bu karakterlerden biri olmayan ilk karakteri bulacaktır. if(auto idx = str.find_last_not_of("aeiou"); idx != std::string::npos) { std::cout << "Bulundu. idx : " << idx << "\n"; // OUTPUT => Bulundu. idx : 13 str[idx] = '*'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : trxmoprnwertq* } else { std::cout << "Bulunamadı.\n"; } } >>>> 'string' sınıfındaki atama fonksiyonları: >>>>> '.operator=(const std::string&)' fonksiyonu: * Örnek 1, #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : std::string strTwo{"Taceddin"}; str = strTwo; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [8] : Taceddin } >>>>> '.operator=(std::string&&)' fonksiyonu: * Örnek 1, #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : std::string strTwo{"Taceddin"}; str = std::move(strTwo); std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [8] : Taceddin std::cout << "[" << strTwo.size() << "] : " << strTwo << "\n"; // OUTPUT => [0] : } >>>>> '.operator=(std::initializer_list&)' fonksiyonu: * Örnek 1, #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : str = {'o', 'k', 'a', 'n'}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [4] : okan } >>>>> '.operator=(const char*)' fonksiyonu: * Örnek 1, #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : str = "serafettin"; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [10] : serafettin } >>>>> '.operator=(char)' fonksiyonu : Her ne kadar 'char' parametreli 'Ctor' olmasa bile, iş bu parametreli atama operatörü mevcuttur. * Örnek 1, #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : str = 'X'; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [1] : X } >>>>> '.assign()' fonksiyonu : Bünyesinde birden fazla 'overload' barındırır. Bunlar 'fill' parametreli, 'range' parametreli, 'C-String' parametreli, 'Data' parametreli vs. fonksiyonlardır. Daha kompleks atamaları yapmamıza imkan sağlar. * Örnek 1, 'C-String' parametreli versiyonu: #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : char buffer[] = "Muhsin Gozenek"; str.assign(buffer); std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [14] : Muhsin Gozenek } * Örnek 2, 'fill' parametreli versiyonu: #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : str.assign(9, 'E'); std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [9] : EEEEEEEEE } * Örnek 3, 'range' parametreli versiyonu: #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : char buffer[] = "Muhsin Gozenek"; str.assign(buffer, buffer + 6); // Birinci ve ikinci adresler arasındaki karakterler. std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [6] : Muhsin } * Örnek 4, 'Data' parametreli versiyonu: #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : char buffer[] = "Muhsin Gozenek"; str.assign(buffer + 7, 3); // Birinci adresten başlayıp ilk üç karakter std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [3] : Goz } * Örnek 5, İlgili indisten başlayıp, yazının geri kalanı. #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : std::string strTwo = "Muhsin Gozenek"; str.assign(strTwo, 10); // Onuncu indisten başlayarak, yazının geri kalanı std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [4] : enek } * Örnek 6, İlgili indisten başlayıp belirli bir karakter adedince. #include #include int main() { std::string str{}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : std::string strTwo = "Muhsin Gozenek"; str.assign(strTwo, 10, 2); // Onuncu indisten başlayarak, ilk iki karakterlik yazı. std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [2] : en } >>>> 'string' sınıfının '.resize()' fonksiyonu: Kaptaki öğe sayısını değiştirmektedir. Var olan büyüklük değerinden daha büyük bir rakam argüman olarak geçilirse, fazlalık olan karakterler 'null-char.' değerinde olacaklardır. Bir tane 'overload' ı vardır ki o fonksiyon ile fazlalık karakterlerin ne olması gerektiğine karar verebiliyoruz. Bu fonksiyon ile 'string' nesnesinin uzunluğunu da KÜÇÜLTEBİLİR. 'capacity' bilgisi yazı küçülürken DEĞİŞMEME GARANTİSİ VAR fakat yazı büyürken de duruma göre ya DEĞİŞİYOR ya da DEĞİŞMİYOR. * Örnek 1, #include #include int main() { std::string str{"Muhsin Gozenek"}; std::cout << "[" << str.size() << "] : " << str << "\n"; // OUTPUT => [0] : str.resize(20); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [40] : Muhsin Gozenek str.resize(40, 'x'); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [40] : [Muhsin Gozenekxxxxxxxxxxxxxxxxxxxx] str.resize(6); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [6] : [Muhsin] } >>>> 'string' sınıfındaki karşılaştırma fonksiyonları: Bir başka 'string' türünden nesne ile veya bir 'C-String' ile karşılaştırmaya olanak veren fonksiyonlardır. Bir 'karakter' ile karşılaştırma yapan fonksiyon YOKTUR. >>>>> Karşılaştırma operatörleri arka planda 'global operator functions' çağırdıklarından, ilk operatörün 'string' sınıf türünden bir nesne olması da gerekmemektedir. 6 operatör için de 'global operator functions' vardır. Birden fazla 'overload' versiyonları vardır. Geri dönüş değeri 'bool' türündendir. >>>>> Ek olarak 'member function' olan '.compare()' fonksiyonu da vardır ki daha kompleks karşılaştırmalarda işimizi görür. C dilindeki 'strcmp()' fonksiyonu ile benzer arayüze sahiptir. Bu fonksiyonun da 'overload' ı vardır. Geri dönüş değeri de 'int' türdendir. >>>> 'string' sınıfındaki '.swap()' fonksiyonu: İlgili sınıf nesnesi içerisindeki yazılar birbiri ile değiştirilir. * Örnek 1, #include #include int main() { /* # OUTPUT # ------------- &x : 0x7ffc28ca5a60=> ali &y : 0x7ffc28ca5a80=> ahmet ------------- &x : 0x7ffc28ca5a60=> ahmet &y : 0x7ffc28ca5a80=> ali ------------- &x : 0x7ffc28ca5a60=> ali &y : 0x7ffc28ca5a80=> ahmet ------------- */ std::cout << "-------------\n"; std::string x{"ali"},y{"ahmet"}; std::cout << "&x : " << &x << "=> " << x << "\n"; std::cout << "&y : " << &y << "=> " << y << "\n"; std::cout << "-------------\n"; // KAÇINMAMIZ GEREKEN 'swap' YÖNTEMİ. ÇÜNKÜ BURADA KOPYALAMA YAPILMAKTADIR. auto temp = x; x = y; y = temp; std::cout << "&x : " << &x << "=> " << x << "\n"; std::cout << "&y : " << &y << "=> " << y << "\n"; std::cout << "-------------\n"; // KULLANMAMIZ GEREKEN YÖNTEM: x.swap(y); std::cout << "&x : " << &x << "=> " << x << "\n"; std::cout << "&y : " << &y << "=> " << y << "\n"; std::cout << "-------------\n"; } >>>> 'sub-string' elde etme fonksiyonları ; >>>>> '.substr()' fonksiyonu : Birinci parametresi bizden bir indeks istemektedir. İkinci parametre ise o indisten itibaren kaç karakteri kapsayağının bilgisini istemektedir. Eğer ikinci parametre boş kalırsa varsayılan olarak 'std::string::npos' değeri geçilir ki bu da geri kalan bütün karakterleri kapsar. Eğer ilk parametre boş kalırsa, ki bu durumda varsayılan değer olarak '0' değeri geçilecektir, yazının bir kopyasını çıkartmış oluruz. * Örnek 1, #include #include int main() { std::string name{"berker boyaci, alper oner"}; auto str = name.substr(7, 6); // Yedinci indisten baslayıp, ilk alti karakter auto strTwo = name.substr(15); // Onbeşinci indisten başlayıp, yazının geri kalan kısmı. std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [25] : [berker boyaci, alper oner] std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [6] : [boyaci] std::cout << "[" << strTwo.size() << "] : [" << strTwo << "]\n"; // OUTPUT => [10] : [alper oner] } >>>> 'string' sınıfındaki iteratör ile yapılan EKLEME ve SİLME İŞLEMLERİ; >>>>> '.insert()' fonksiyonu, birden fazla 'overload' mevcuttur. * Örnek 1, #include #include int main() { std::string name{"berker boyaci"}; name.insert(name.begin() + 3, '!'); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [14] : [ber!ker boyaci] } * Örnek 2, #include #include int main() { std::string name{"berker boyaci"}; name.insert(name.begin() + 3, {'Q', 'Q', 'Q'}); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [16] : [berQQQker boyaci] } * Örnek 3, #include #include int main() { std::string name{"berker boyaci"}; name.insert(name.begin() + 3, 12, 'Q'); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [25] : [berQQQQQQQQQQQQker boyaci] } >>>>> '.erase()' fonksiyonu, birden fazla 'overload' içermekte olup, verilen konumdaki öğeyi silmektedir. * Örnek 1, #include #include int main() { std::string name{"berker boyaci"}; name.erase(name.begin() + 3); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [12] : [berer boyaci] } * Örnek 2, #include #include int main() { std::string name{"berker boyaci"}; name.erase(name.begin() + 3, name.begin() + 10); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [6] : [beraci] } >>>> 'string' sınıfındaki "indeks ile ekleme ve silme işlemleri" : Bu işlemlerde kullanılan fonksiyonlar da birden fazla 'overload' içermektedir. >>>>> 'insert' işleminde kullanılacak: Birinci parametre 'insert' edilecek yerin konumu. Diğer parametreler ise neyin 'insert' edileceğine dair parametrelerdir. Geri dönüş değeri '*this'. * Örnek 1, #include #include int main() { std::string name{"0123456789"}; name.insert(4, "ali"); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [13] : [0123ali456789] } * Örnek 2, #include #include int main() { std::string name{"0123456789"}; std::string surname{"mustafa"}; name.insert(4, surname); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [17] : [0123mustafa456789] } * Örnek 3, #include #include int main() { std::string name{"0123456789"}; std::string surname{"mustafa"}; name.insert(4, 8, '!'); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [18] : [0123!!!!!!!!456789] } * Örnek 4, #include #include int main() { std::string name{"0123456789"}; std::string surname{"mustafa"}; name.insert(0, surname).insert(8, "ceyda"); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [22] : [mustafa0ceyda123456789] } >>>>> 'erase' işleminde kullanılacak: Birinci parametre konum bilgisidir fakat bu konum bilgisi SİLMEZ. 'overload' versiyonlarına göre bu indisten başlayarak silme işlemini gerçekleştirir. Birinci parametre için varsayılan konum bilgisi 'sıfır' konumudur. Yani bu durumda yazının hepsini silecektir. İkinci karakter ise toplamda kaç adet karakterin silineceğinin bilgisini içermektedir. * Örnek 1, #include #include int main() { std::string name{"0123456789"}; std::string surname{"mustafa"}; name.erase(5); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [5] : [01234] name.erase(); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [0] : [] } * Örnek 2, #include #include int main() { std::string name{"0123456789"}; std::string surname{"mustafa"}; name.erase(5, 3); std::cout << "[" << name.size() << "] : [" << name << "]\n"; // OUTPUT => [7] : [0123489] } * Örnek 3, #include #include int main() { std::string str{"dolar kuru hizli bir sekilde yukselirken, enflasyon da tirmaniyor."}; std::string strToDelete; std::cout << "Silinecek Yazi : " ; std::cin >> strToDelete; // INPUT => hizli std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [66] : [dolar kuru hizli bir sekilde yukselirken, enflasyon da tirmaniyor.] if(auto idx = str.find(strToDelete); idx != std::string::npos) { str.erase(idx, strToDelete.size()); } else { std::cout << "[" << strToDelete << "] yazisi bulunamadi.\n"; } std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [61] : [dolar kuru bir sekilde yukselirken, enflasyon da tirmaniyor.] } * Örnek 4, Mülakat Sorusu : Aşağıdaki diziyi birden fazla yöntem ile silmeyi deneyin. #include #include int main() { std::string str{"mustafa erman"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [13] : [mustafa erman] // str.clear(); // I // str.erase(str.begin(), str.end()); // II : Iterator interface // str.erase(); // III : Index interface // str = ""; // IV : Boş bir yazı atarsak. // str = {}; // V // str.resize(0); // VI // str = std::string{}; // VII : Geçici nesnenin değerini atadığımız için 'Move Ctor' çağrılacak ve o nesnenin değerini // çalacak. std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [0] : [] } >>>> '.push_back()' ve '.pop_back()' fonksiyonları: Sırasıyla sondan ekleme ve çıkarma yapan bir fonksiyonlardır. * Örnek 1, #include #include int main() { std::string str{"mustafa erman"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [13] : [mustafa erman] str.push_back('c'); str.push_back('c'); str.push_back('c'); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [16] : [mustafa ermanccc] str.pop_back(); str.pop_back(); str.pop_back(); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [13] : [mustafa erman] } * Örnek 2, #include #include int main() { /* # OUTPUT # [26] : [mustafa erman sen cok yasa] [25] : [mustafa erman sen cok yas] [24] : [mustafa erman sen cok ya] [23] : [mustafa erman sen cok y] [22] : [mustafa erman sen cok ] [21] : [mustafa erman sen cok] [20] : [mustafa erman sen co] [19] : [mustafa erman sen c] [18] : [mustafa erman sen ] [17] : [mustafa erman sen] [16] : [mustafa erman se] [15] : [mustafa erman s] [14] : [mustafa erman ] [13] : [mustafa erman] [12] : [mustafa erma] [11] : [mustafa erm] [10] : [mustafa er] [9] : [mustafa e] [8] : [mustafa ] [7] : [mustafa] [6] : [mustaf] [5] : [musta] [4] : [must] [3] : [mus] [2] : [mu] [1] : [m] */ std::string str{"mustafa erman sen cok yasa"}; while(!str.empty()) { std::cout << "[" << str.size() << "] : [" << str << "]\n"; str.pop_back(); // str.erase(0,1); // Alternatif II : Sıfırıncı indisten başla ve bir karakter sil. } } * Örnek 3, #include #include int main() { /* # OUTPUT # [26] : [mustafa erman sen cok yasa] [25] : [ustafa erman sen cok yasa] [24] : [stafa erman sen cok yasa] [23] : [tafa erman sen cok yasa] [22] : [afa erman sen cok yasa] [21] : [fa erman sen cok yasa] [20] : [a erman sen cok yasa] [19] : [ erman sen cok yasa] [18] : [erman sen cok yasa] [17] : [rman sen cok yasa] [16] : [man sen cok yasa] [15] : [an sen cok yasa] [14] : [n sen cok yasa] [13] : [ sen cok yasa] [12] : [sen cok yasa] [11] : [en cok yasa] [10] : [n cok yasa] [9] : [ cok yasa] [8] : [cok yasa] [7] : [ok yasa] [6] : [k yasa] [5] : [ yasa] [4] : [yasa] [3] : [asa] [2] : [sa] [1] : [a] */ std::string str{"mustafa erman sen cok yasa"}; while(!str.empty()) { std::cout << "[" << str.size() << "] : [" << str << "]\n"; str.erase(str.begin()); // str.erase(0,1); // Alternatif I : Sıfırıncı indisten başla ve bir karakter sil. } } >>>> '.replace()' : '.erase()' & '.insert()' fonksiyonlarının işini aynı ayna yapmaktadır. Birden çok 'overload' mevcuttur. Her zaman için ilk parametre "neyin yerine gelecektir 'insert' edeceğimiz karakter?" sorusuna cevap verir. * Örnek 1, #include #include int main() { std::string str{"mustafa erman sen cok yasa"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [mustafa erman sen cok yasa] str.replace(0, 7, "Kandemir"); // Sıfırıncı indisten başla, ilk yedi karakterlik yazı yerine "Kandemir" yazısını ekle std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [27] : [Kandemir erman sen cok yasa] } >>>> 'string' sınıfındaki 'Tür Dönüştürme' operatörleri: >>>>> 'to_string()' : Global bir fonksiyondur. Temel türlerin her birisi için çağrılabilir. Geri dönüş değeri 'std::string' şeklindedir. C++11 ile dile eklenmiştir. >>>>> 'stoi()' : Global bir fonksiyondur. 'std::string' türünden nesneleri, 'int' türe dönüştürür. Yazı içindeki harflare ellemez. İlk parametresi dönüştürülecek yazı, ikinci parametresi bir indeks, üçüncü parametre ise ilgili rakam için kaçlık taban bilgisi. İkinci parametreye geçilen adrese, en son dönüştürülen rakamın adresini yazmaktadır. * Örnek 1, #include #include int main() { std::string str{"123Emre"}; size_t idx; auto ival = std::stoi(str); std::cout << "ival : " << ival << "\n"; // OUTPUT => ival : 123 ival = std::stoi(str, &idx); std::cout << "ival : " << ival << "\n"; // OUTPUT => ival : 123 std::cout << "idx : " << idx << "\n"; // OUTPUT => idx : 3 ival = std::stoi(str, &idx, 16); std::cout << "ival : " << ival << "\n"; // OUTPUT => ival : 4670 std::cout << "idx : " << idx << "\n"; // OUTPUT => idx : 4 // İlgili yazıdaki 'E' karakteri de dahil edildi. } >> 'Init. List' sınıfının incelenmesi : Başlık dosyası 'initializer_list' isimli başlık dosyasında tanımlanmıştır. Bir sınıf şablonudur. >>> Açıklayıcı örnekler: * Örnek 1, //.. int main() { std::initializer_list myList{2, 3, 4, 5, 6, 5, 4, 3, 2}; auto n = {2, 3, 4, 5, 6, 5}; // 'n' değişkeninin türü : 'initializer_list' in 'int' açılımı şeklinde. std::cout << "type : <" << typeid(n).name() << ">\n"; // OUTPUT => type : auto m = {2.2, 3.3, 4.4, 5.5}; // 'm' değişkeninin türü : 'initializer_list' in 'double' açılımı şeklinde. std::cout << "type : <" << typeid(n).name() << ">\n"; // OUTPUT => type : auto q = {10.10f}; // 'q' değişkeninin türü : 'initializer_list' in 'float' açılımı şeklinde. std::cout << "type : <" << typeid(q).name() << ">\n"; // OUTPUT => type : auto p{10L}; // 'p' değişkeninin türü : 'long' şeklinde. Artık tür çıkarımı 'initializer_list' yönünde değil, // 'primitive' tür yönünde yapıldı. std::cout << "type : <" << typeid(p).name() << ">\n"; // OUTPUT => type : auto l{10,20}; // SENTAKS HATASI. => error: direct-list-initialization of ‘auto’ requires // exactly one element [-fpermissive] } >>> Elimizde bu sınıf türünden bir nesne var ise ne yapabiliriz? El-cevap: Bu sınıf türü bünyesinde üç-dört adet fonksiyon barındırır. Bunlar '.size()', '.begin()' ve '.end()' şeklinde. * Örnek 1, #include int main() { std::initializer_list myList{2, 3, 4, 5, 6, 5, 4, 3, 2}; std::cout << "size of list : " << myList.size() << "\n"; // OUTPUT => size of list : 9 // Geleneksel Yaklaşım: for(auto first = myList.begin(); first != myList.end(); ++first) { std::cout << "[" << *first << "] "; // OUTPUT => [2] [3] [4] [5] [6] [5] [4] [3] [2] } std::cout << "\n"; // Modern Yaklaşım: for(auto index : myList) { std::cout << "[" << index << "] "; // OUTPUT => [2] [3] [4] [5] [6] [5] [4] [3] [2] } std::cout << "\n"; // Modern Yaklaşım v2: for(auto index : {2, 3, 4, 5, 6, 5, 4, 3, 2}) { std::cout << "[" << index << "] "; // OUTPUT => [2] [3] [4] [5] [6] [5] [4] [3] [2] } } >>> Elemanlarının birer 'Sabit-İfadesi' olma zorunluluğu YOKTUR. * Örnek 1, #include void func(int a, int b, int c, int d) { std::initializer_list myList{a,b,c,d}; // Legal for(auto index : myList) { std::cout << "[" << index << "] "; } } void func(const std::initializer_list& myList) { for(auto index : myList) { std::cout << "[" << index << "] "; // Legal } } int main() { func(1,2,3,4); // OUTPUT => [1] [2] [3] [4] func({9,8,7,6}); // OUTPUT => [9] [8] [7] [6] } >>> 'initializer_list' her zaman için 'const'. Dolayısıyla elemanlarına bir atama yapamıyoruz. Elemanları sadece okuma amaçlı kullanabiliriz. * Örnek 1, #include #include int main() { auto myList = {2,3,4,5}; auto iter = myList.begin(); std::cout << "first : " << *iter << "\n"; *iter = 10; // error: assignment of read-only location ‘* iter’ std::cout << "first : " << *iter << "\n"; } >>> İki adet 'initializer_list' i birbirine kopyaladığımız zaman iki adet ayrı arka-plan-dizisi ortada yok. Referans semantiği ile sınıf nesneleri birbirine kopyalanmakta. >>> Herhangi bir sınıfın hem 'initializer_list' parametreli hem de 'primitive' parametreli bir 'Ctor' olsun. '{}' çifti ile nesne hayata getirdiğimiz zaman 'initializer_list' parametreli 'Ctor' un çağrılma önceliği vardır. * Örnek 1, #include #include class Myclass{ public: Myclass(int x) { std::cout << "Myclass(int x) was called.\n"; } Myclass(int x, int y) { std::cout << "Myclass(int x, int y) was called.\n"; } Myclass(std::initializer_list iList) { std::cout << "Myclass(std::initializer_list iList) was called.\n"; } }; int main() { // Myclass m = {1, 2, 3, 4}; // OUTPUT => Myclass(std::initializer_list iList) was called. // Myclass mm{1, 2, 3, 4}; // OUTPUT => Myclass(std::initializer_list iList) was called. // Myclass m(12); // OUTPUT => Myclass(int x) was called. // Myclass mm{12}; // OUTPUT => Myclass(std::initializer_list iList) was called // Myclass m(12,21); // OUTPUT => Myclass(int x, int y) was called. // Myclass mm{12,21}; // OUTPUT => Myclass(std::initializer_list iList) was called Myclass m = 12; // OUTPUT => Myclass(int x) was called. Myclass mm = {12}; // OUTPUT => Myclass(std::initializer_list iList) was called. Myclass mmm = {12,24}; // OUTPUT => Myclass(std::initializer_list iList) was called. } >> 'STL Containers' incelenmesi. Bunlar, üç ana gruba ayrılırlar. >>> Sequence Containers : Bunlar da kendi içinde birden fazla öğe içerir. Bunlar 'Vector', 'Deque', 'List', 'Forward_List', 'String', 'Array', 'C-Array' öğelerinden oluşur. Bir takım fonksiyonlar sadece bu kaba özel fonksiyonlardır. Örneğin, >>>> '.back()' fonksiyonu, kaptaki son öğeye erişir. >>>> '.front()' fonksiyonu, kaptaki ilk öğeye erişmektedir. >>>> '.resize()' fonksiyonu, kaptaki öğe sayısını değiştirmektedir. >>> Associative Containers : Bunlar da kendi içinde birden fazla öğe içerir. Bunlar 'Set', 'Multiset', 'Map', 'Multimap' öğelerinden oluşur. >>> Unordered Associative Containers : Bunlar da kendi içinde birden fazla öğe içerir. Bunlar 'Unoredered_Set', 'Unoredered_Multiset', 'Unoredered_Map', 'Unoredered_Multimap' öğelerinden oluşur. >>> İş bu bütün kaplarda ortak olan bazı fonksiyonlar vardır ki bunlar >>>> '.empty()' fonksiyonu, ilgili kabın boş olup olmadığını sınar. 'bool' türünden değer döndürür. >>>> '.size()' fonksiyonu, ilgili kaptaki eleman sayısını döndüren bir fonksiyondur. 'string' sınıfında alternatif olarak '.length()' fonksiyonu da vardır. >>>> '.clear()' fonksiyonu kapta bulunan bütün öğeleri siler, kaptan çıkartır. >>>> '.swap()' fonksiyonu kaptaki öğeleri değil, sınıfın içerisindeki 'member' lar takas edilir. Varsayılan olarak 'lexicographical_compare' algoritması kullanılır. >>>>> 'lexicographical_compare' : Öğeler karşılıklı olarak karşılaştırılırken öğeler birbirine eşit ve öğe sayısı da birbiri ile aynı ise bu durumda bunlar birbirine eşittir. Eğer öğelerin elemanları daha bitmemişken, eşit olmayan bir eleman çifti görülürse, elemanı büyük olan öğe daha büyüktür. Karşılaştırma devam ederken bir öğenin elemanlarının bitmesi durumunda, o öğe küçük öğe seçilir. * Örnek 1, //.. Öğe 1 : 1 2 3 4 Öğe 2 : 1 2 3 4 // Yukarıdaki Öğe 1 ve Öğe 2'nin eleman sayısı ve elemanlarının kendileri de birbirine eşit // olduğundan, Öğe 1 ve Öğe 2 birbirine eşittir diyebiliriz. * Örnek 2, Öğe 1 : 1 2 3 4 Öğe 2 : 1 2 5 // Yukarıdaki Öğe 1 ve Öğe 2'nin ikinci indislerindeki elemanlar birbirine eşit değil. Öğeler daha // eleman da barındırmaktadır. Dolayısıla '5' > '3' olacağından, Öğe 2 daha büyüktür. * Örnek 3, Öğe 1 : 1 2 3 4 Öğe 2 : 1 2 3 // Yukarıdaki karşılaştırma üçüncü indisten sonra bitecektir. Öğe 1'in daha elemanı olduğundan, // Öğe 1 daha büyüktür. * Örnek 4, #include #include int main() { std::vector iVecOne(100000, 1); std::cout << "iVecOne.size() : " << iVecOne.size() << "\n"; // OUTPUT => iVecOne.size() : 100000 std::vector iVecTwo(1, 2); std::cout << "iVecTwo.size() : " << iVecTwo.size() << "\n"; // OUTPUT => iVecTwo.size() : 1 std::cout << std::boolalpha << (iVecOne < iVecTwo) << "\n"; // OUTPUT => true } >>> Konum tutan değişkenlere 'iterator' denmekte olup, 'STL' kaplarında Ekleme ve Silme işlemlerinde kullanılırlar. Birinci parametre her zaman ekleme yapılacak konum bilgisini, ikinci parametre ise eklenecek değeri. Örneğin, '.insert()' ve '.erase()' fonksiyonları ki '.erase()' fonksiyonu 'std::array' sınıf türünde YOKTUR. '.insert()' fonksiyonunun geri dönüş değeri duruma göre ya '*this' ya da bir iteratördür. >>> '.maxsize()' fonksiyonu 'const' bir üye fonksiyon olup, kapta tutulabilecek maksimum öğe sayısını döndürür. >> 'algorithm' başlık dosyasındaki bazı fonksiyonların incelemesi: >>> 'reverse()' fonksiyonu : Argüman olarak aldığı ifadeyi tersine çevirmektedir. * Örnek 1, #include #include #include int main() { std::string str{"mustafa erman sen cok yasa"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [mustafa erman sen cok yasa] reverse(str.begin(), str.end()); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [asay koc nes namre afatsum] } >>> 'sort()' fonksiyonu : Argüman olarak aldığı ifadedeki karakterleri ASCII tablosuna göre sıralamaktadır. * Örnek 1, #include #include #include int main() { std::string str{"mustafa erman sen cok yasa"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [mustafa erman sen cok yasa] sort(str.begin(), str.end()); std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [ aaaaaceefkmmnnorssstuy] } >>> 'remove()' fonksiyonu : Argüman olarak aldığı ifadede silme işlemi yapmaktadır ama LOJİK SİLME İŞLEMİDİR. * Örnek 1, #include #include #include int main() { std::string str{"mustafa erman sen cok yasa"}; std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [26] : [mustafa erman sen cok yasa] char c = 'a'; // The character to be deleted. str.erase( remove(str.begin(), str.end(), c), str.end() ); // Remove-Erase idiom. std::cout << "[" << str.size() << "] : [" << str << "]\n"; // OUTPUT => [21] : [mustf ermn sen cok ys] } >> C++ 17 ile gelen 'invoke()' fonksiyonu: Bir sınıf türünden nesneye ve bir fonksiyon adresini argüman olarak alan, sonrasında da bu fonksiyonu çağıran fonksiyondur. 'functional' başlık dosyasında bildirilmiştir. * Örnek 1, #include #include class Base { public: void func() { std::cout << "void Base::func() was called. this => " << this << "\n"; } }; class Der : public Base { }; int main() { /* # OUTPUT # &[myDer] : 0x7ffc12226cff void Base::func() was called. this => 0x7ffc12226cff */ Der myDer; std::cout << "&[myDer] : " << &myDer << "\n"; std::invoke(&Base::func, myDer); } >> Esasında bu 'STL' kütüphanesi nesne yönelimli bir kütüphaneden ziyade jenerik bazlı bir kütüphane olup, 'Containers', 'Iterators', 'Algorithms', 'Adapters', 'Function Objects' vb. şeklinde bölümlerden oluşmaktadır. Bu bölümleri kabaca açıklayacaksak; >>> 'Containers' grubu veri yapılarını temsil etmektedirler. Kendi içinde üç gruba ayrılmışlardır. Bu gruplar, 'Sequence Containers', 'Associative Containers' ve 'Unordered Associative Containers' şeklinde gruplardır. Bu üç grup ise şu temel sınıf şablonlarını bünyelerinde barındırmaktadır. Bunlar; >>>> 'Sequence Containers' : Bu gruptakiler ise 'std::vector', 'std::deque', 'std::list', 'std::forward_list', 'std::array' ve 'std::string' sınıf şablonlarından meydana gelmiştir. Bunlardan, >>>>> 'std::vector' : Dinamik dizi veri yapısıdır. >>>>> 'std::deque' : Dinamik dizilerin dizisi şeklinde bir veri yapısıdır. >>>>> 'std::list' : Çifte bağlı liste veri yapısıdır. >>>>> 'std::forward_list' : Tekli bağlı liste veri yapıdır. >>>>> 'std::array' : C dilindeki dinamik olmayan dizilerin bir sınıf ile kaplanmış halidir. >>>>> 'std::string' : Dinamik dizi veri yapısıdır. Karakterler ile uğraşmaktadır. >>>> 'Associative Containers' : 'std::set', 'std::multiset', 'std::map' ve 'std::multimap' sınıf şablonlarından, >>>>> 'std::set' : İkili arama ağacının implementasyonudur. Bünyesinde sadece o türden bir tane değer tutar. >>>>> 'std::multiset' : 'std::set' den farkı, o türden birden fazla değeri bünyesinde tutabilmektedir. >>>>> 'std::map' : Eşlemede kullanılır. Bünyesinde o türden sadece bir çift bulunur('key-value' pair). >>>>> 'std::multimap' : 'std::map' den farkı, o türden birden fazla çift bulundurabilir. >>>> 'Unordered Associative Containers' : 'std::unordered_set', 'std::unordered_multiset', 'std::unordered_map' ve 'std::unordered_multimap' sınıf şablonlarından, >>>>> Diğer programlama dillerindeki 'hash_set', 'hash_map' veri yapılarının C++ dilindeki karşılığıdır. >>> 'Iterators' grubu göstericilerin daha soyutlaştırılmış halleridir. Kaplardaki öğelerin konumlarını tutma amacı taşırlar. Kapların 'nested-type' ları olacak şekilde implementasyonları yapılmıştır. Yine kaplar gibi sınıf şablonlarıdır. Her kapta bulunurlar. Detaylı inceleme için aşağıdaki örneğin inceleyelim: * Örnek 1, #include #include #include #include template void print_range(Iter beg, Iter end) { while(beg != end) std::cout << *beg++ << " "; std::cout << "\n"; } // Buradaki 'Iter' bir şablon parametresidir. 'Iter' e karşılık gelen şeyler // i. 'operator!=' fonksiyonunu 'overload' etmeli veya operand olabilmeli, // ii. 'operator*' ve 'operator++' fonksiyonlarını 'overload' etmeli veya bunlara operand olabilmeli, // iii. 'operator<<' fonksiyonunu 'overlaod' etmeli veya buna operand olabilmeli. // Aksi halde sentaks hatası alacağız. // Derleyicinin şablondan hareketle 'I' nolu çağrı için yazacağı fonksiyon: /* void print_range(int* beg, int* end) { while(beg != end) std::cout << *beg++ << " "; std::cout << "\n"; } */ // Derleyicinin şablondan hareketle 'II' nolu çağrı için yazacağı fonksiyon: /* void print_range(std::vector::iterator beg, std::vector::iterator end) { while(beg != end) std::cout << *beg++ << " "; std::cout << "\n"; } */ int main() { /* # OUTPUT # 1 4 7 2 31 1 4 7 2 31 1 4 7 2 31 ali veli merve ayse metin */ int a[] = { 1, 4, 7, 2, 31 }; print_range(a, a + 5); // I : Tür çıkarımı yapılacak. 'Iter' ile birlikte '&' deklaratörü kullanılmadığından, // 'array-decay' mekanizması çalışacaktır. std::vector ivec{ 1, 4, 7, 2, 31 }; print_range(ivec.begin(), ivec.end()); // II : 'Iter' için tür çıkarımı bu sefer 'iterator' şeklinde yapılacaktır. std::list ilist{ 1, 4, 7, 2, 31 }; print_range(ilist.begin(), ilist.end()); // III : 'Iter' için tür çıkarımı bu sefer 'iterator' şeklinde yapılacaktır. std::list myNameList{ "ali", "veli", "merve", "ayse", "metin" }; print_range(myNameList.begin(), myNameList.end()); // Çıktıdan da görüldüğü üzere tek bir fonksiyon şablonu birbirinden farklı türler için kullanıldı. // Peki bu nasıl mümkün oldu? El-cevap : Aşağıdaki örneği incelediğim. } * Örnek 2, Kapların ve iteratörlerin temsili gösterimi: template class Vector{ public: class Iterator{ public: T& operator*(); Iterator& operator++(); Iterator operator++(int); T* operator->(); }; //.. Iterator begin(); // Kapta tutulan ilk öğeyi gösteren bir iteratör döndürür. Iterator end(); // Kapta tutulan en sonki öğenin konumundan bir sonraki konumu gösteren bir iteratör döndürür. // Dolayısıyla 'derefence' EDEMEYİZ çünkü geçerli bir konum değildir. Karşılaştırma amacıyla // kullanabiliriz. }; //.. int main() { std::vector ivec{ 1, 4, 7, 9, 2 }; auto iter = ivec.begin(); // std::vector::iterator iter = ivec.begin(); std::cout << *iter << "\n"; // Konumu tutulan nesneye eriştik ve onu yazdırdık. ++iter; // Konum bilgisini bir arttırdık. std::cout << *iter << "\n"; // Konumu tutulan nesneye eriştik ve onu yazdırdık. for(auto iter = ivec.begin(); iter != ivec.end(); ++iter) std::cout << *iter << "\n"; } >>>> Bir iteratörü 'derefence' ettiğimiz zaman o konumda nesne bulunması koşulunu sağlaması gereken BİZLERİZ. O konumda geçerli bir nesne olmadığında ilgili iteratöre erişmeye çalışmak 'Tanımsız Davranış' olacaktır. * Örnek 1, //.. int main() { std::vector ivec; auto iter = ivec.begin(); std::cout << *iter << "\n"; // Bu noktada tanımsız davranış olacaktır. } >>>> 'range' bilgisi iki konum arasındaki mesafe demektir. Fakat bu konum bilgilerinden ilki 'range' içerisindeki ilk öğeyi kastetmekteyken, ikinci konum bilgisi ise 'range' içerisindeki son öğeden sonraki öğenin bulunduğu konumu kastetmektedir. * Örnek 1, template void print_range(Iter beg, Iter end) { while(beg != end) std::cout << *beg++ << " "; std::cout << "\n"; } int main() { int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; print_range( a, a + 10 ); // 'a' birinci konum bilgisi demektir. Kaptaki ilk öğenin konumudur. // 'a + 10' ise ikinci konum bilgisi demektir. Kaptaki son öğeden sonraki öğenin konumudur. // Yukarıdaki iki konum arası ise bizim 'range' dediğimiz bölümdür. // Çıktı => 0 1 2 3 4 5 6 7 8 9 print_range( a + 5, a + 8 ); // 'a + 5' konumu dahil, 'a + 8' konumu hariç, bu iki konum arasındaki nesneleri yazdıracaktır. // Çıktı => 5 6 7 } >>>>> 'range' durumunun geçerli olması için ilk konum bilgisini tutan değişkeni '++' operatörünün operandı yaptığım zaman bir müddet sonra ikinci konum bilgisine EŞİT HALE GELMESİ ZORUNLUDUR. Aksi durumda geçerli bir 'range' söz konusu değildir. * Örnek 1, template void print_range(Iter beg, Iter end) { while(beg != end) std::cout << *beg++ << " "; std::cout << "\n"; } int main() { int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; print_range( a + 5, a + 2 ); // 'a + 5' konumundan başlamamıza rağmen hiç bir zaman 'a+2' konumuna ulaşamayacağız. Dolayısıyla // 'Tanımsız Davranış' olacaktır. } >>>> C-style dizilerin bittiği yerin adresi 'derefence' edilemezler fakat karşılaştırma amacıyla kullanılabilirler. >>>> Peki bir iteratör nesnesi ile neler yapabiliriz? El-cevap: İteratörlerin kategorilerine göre değişmektedir. Genel hatlarıyla iteratörler şu kategorilerden birine ait olmak zorundadır; >>>>> 'output_iterator' : 'output_iterator_tag' türünün 'typedef' halidir. Bu '_tag' isimleri boş bir 'struct' şeklindedir. 'ostream_iterator' ve 'ostreambuf_iterator' grupları, bu tip iteratörlere sahiptirler. Bu tip iteratörler 'Copy Constructable' ama 'Default Constructable' DEĞİLDİR. 'operator++' ve 'operator++(int)' operatörlerine, 'operator*'(derefence) ve 'operator->' operatörlerine operand olabiliyor. >>>>> 'input_iterator' : 'input_iterator_tag' türünün 'typedef' halidir. Bu '_tag' isimleri boş bir 'struct' şeklindedir. 'istream_iterator' ve 'istreambuf_iterator' grupları, bu tip iteratörlere sahiptirler. Bu tip iteratörler, 'output_iterator' grubunun yapabildiklerini yapabilmektedirler. Ek olarak 'operator==' ve 'operator!=' operatörlerine de operand olabiliyorlar. >>>>> 'forward_iterator' : 'forward_iterator_tag' türünün 'typedef' halidir. Bu '_tag' isimleri boş bir 'struct' şeklindedir. 'forward_list', 'unordered_map', 'unordered_multimap', 'unordered_set' ve 'unordered_multiset' grupları bu tip iteratörlere sahiptirler. Bu tip iteratörler 'input_iterator' ve 'output_iterator' gruplarının yapabildiklerini yapabilmektedir. Ek olarak bu tip iteratörler 'Default Constructable' haldedirler. >>>>> 'bidirectional_iterator' : 'bidirectional_iterator_tag' türünün 'typedef' halidir. Bu '_tag' isimleri boş bir 'struct' şeklindedir. 'list', 'set', 'multiset', 'map' ve 'multimap' grupları bu tip iteratörlere sahiptirler. Bu tip iteratörler 'forward_iterator' grubunun yaptığı her şeyi yapabilmektedir. Ek olarak 'operator--' ve 'operator--(int)' operatörlerine operand olabilmektedirler. >>>>> 'random_access_iterator' : 'random_access_iterator_tag' türünün 'typedef' halidir. Bu '_tag' isimleri boş bir 'struct' şeklindedir. 'vector', 'deque', 'array', 'string' ve 'C-arrays' bu tip iteratör gruplarına sahiptirler. Bu tip iteratörler 'bidirectional_iterator' grubunun yaptığı her şeyi yapabilmektedir. Kendileri birebir 'pointer' gibi davranmaktadırlar. Dolayısıyla ek olarak bir 'C-pointer' ile neler yapabiliyorsak, bu tip iteratörler ile aynılarını yapabiliriz. Dolayısıyla bu kategorideki iteratörlerin de 'interface' bilgileri BİRBİRLERİNDEN FARKLIDIR. Bu kategoriler arasındaki ilişkiyi özetlemek gerekirse; "random_access_iterator( bidirectional_iterator( forward_iterator( input_iterator + output_iterator ) ) )" şeklinde özetleyebilir. Buradan hareketle diyebiliriz ki en geniş yetkilere sahip 'random_access_iterator' iteratör grubudur. 'random_access_iterator', C dilindeki 'göstericiler' ile yapabildiğimiz işlerin aynısını yapabilmektedirler. >>>> Bir iteratörün hangi kategoriden olduğunu saptayabilmek için 'iterator' sınıfının 'nested-type' olan 'iterator_category' türünü kullanmalıyız. Yukarıdaki '_tag' isimlerini 'typeid' operatörünün operandı yaparak. * Örnek 1, #include #include #include #include int main() { std::cout << typeid(std::vector::iterator::iterator_category).name() << "\n"; // OUTPUT => struct std::random_access_iterator_tag std::cout << typeid(std::list::iterator::iterator_category).name() << "\n"; // OUTPUT => struct std::bidirectional_iterator_tag std::cout << typeid(std::forward_list::iterator::iterator_category).name() << "\n"; // OUTPUT => struct std::forward_iterator_tag std::cout << typeid(std::istream_iterator::iterator_category).name() << "\n"; // OUTPUT => struct std::input_iterator_tag } >>>> İteratörlerde 'const' semantiği : Aşağıdaki örnekleri inceleyelim. * Örnek 1, //.. int main() { std::vector iVec{ 1, 2, 3, 4 }; const std::vector::iterator iter = iVec.begin(); // Artık 'iter' isimli iteratörün kendisi 'const' durumdadır. Artık başka bir konum BİLGİSİ TUTAMAZ. // 'high-level pointer' gibidir. *iter = 35; // I: LEGAL ++iter; // II : SENTAKS HATASI. Çünkü 'const' bir nesne ile 'non-const' fonksiyon çağıramayız. // Peki bizler 'I' numaralı durumun sentaks hatası olmasını istiyorsak, bir diğer değiş ile // 'low-level pointer' durumunun temsili olan bir iteratör oluşturmak istiyorsak, ne yapmalıyız? // El-cevap : İkinci örneği inceleyelim. } * Örnek 2, 'const_iterator' türünden nesne oluşturarak, ki bu tip sınıflar 'low-level pointer' mekanizmasını taklit etmektedir. //.. int main() { std::vector iVec{ 1, 2, 3, 4 }; std::vector::const_iterator iter = iVec.begin(); // Artık 'iter' isimli iteratörü sadece salt okuma amaçlı kullanabiliriz. Kendisi 'const' da değildir. std::cout << "*iter : " << *iter << "\n"; // LEGAL. *iter = 35; // I: SENTAKS HATASI. ++iter; // II : LEGAL. // İŞ BU SEBEPETEN ÖTÜRÜ BİR 'range' OKUMA AMACIYLA DOLAŞILIYORSA, 'const_iterator' TÜRÜNDEN İTERATÖRLER // İLE DOLAŞMALIYIZ. Örnek 3'ü inceleyelim. } * Örnek 3, '.cbegin()' gibi üye fonksiyonlar 'const_iterator' sınıf türünden iteratör döndürmektedir. //.. int main() { std::vector iVec{ 1, 2, 3, 4 }; // SALT OKUMA YAPACAKSAK AŞAĞIDAKİ GİBİ KULLANMALIYIZ: for(auto iter = iVec.cbegin(); iter != iVec.cend(); ++iter) std::cout << iter << ", "; } >>>> Herhangi bir 'range' söz konusu olsun. Aşağıdaki örnekleri inceleyelim; Mülakat konusu; * Örnek 1, Kaplarda bulunan '.rbegin()', '.end()' fonksiyonlarının kullanılması: //.. template void print_range(InIter beg, InIter end) { while (beg != end) std::cout << *beg++ << ", "; std::cout << "\n"; } int main() { /* # OUTPUT # 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, */ std::vector iVec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; print_range(iVec.begin(), iVec.end()); print_range(iVec.rbegin(), iVec.rend()); // Peki bu nasıl mümkün hale geldi? El-cevap : Nasıl ki kapların 'iterator' isimli 'nested-type' // sınıfları varsa aynı şekilde 'reverse_iterator' isimli 'nested-type' sınıfları da vardır. } * Örnek 2, İş bu fonksiyonların döndürmüş olduğu iteratörler sırasıyla kaplardaki son ve ilk öğelerin konumlarını tutmaktadır. Yani son öğeden başlıyoruz. //.. int main() { /* # OUTPUT # [9], [8], [8] | [7] | [6] | [5] | [4] | [3] | [2] | [1] | */ std::vector iVec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; std::vector::reverse_iterator iter = iVec.rbegin(); // 'reverse_iterator' sınıfı kalıtım yoluyla 'iterator' sınıfından elde edilmektedir. std::cout << "[" << *iter << "], "; // OUTPUT => 9 ++iter; std::cout << "[" << *iter << "], "; // OUTPUT => 8 std::cout << "\n"; while(iter != iVec.rend()) { std::cout << "[" << *(iter++) << "] | "; } // Peki arka plandaki işleyen mekanizma nasıl bir mekanizma? Her ne kadar dizinin son öğesinden sonraki // öğenin konumu geçerli bir konum olsa da dizinin ilk öğesinden bir önceki öğenin konumu geçerli bir // konum bilgisi değildir. O konum bilgisini kullanmak 'Tanımsız Davranış' oluşturur. // El-cevap : '.rbegin()' fonksiyonunun geri dönüş değeri dizinin bittiği yerin adresi. Tıpkı '.end()' // fonksiyonu gibi. Ama '.operator*' fonksiyonu öyle bir 'overload' edilmiş ki tuttuğu konum bilgisinden // bir önceki konum bilgisindeki nesneye eriştirmektedir. } * Örnek 3, '.operator*()', '.operator++()' fonksiyonlarının incelenmesi: //.. int main() { std::vector iVec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto reverseIter = iVec.rbegin(); // 'reverseIter' aslında konum bilgisi olarak en son öğeden sonraki öğenin konum bilgisini tutmaktadır. std::cout << *reverseIter << "\n"; // Fakat içerik operatörünün operandı olduğunda konumunu tuttuğu öğeye değil, bir önceki konumda bulunan // öğeye eriştirmektedir. // 'reverse_iterator' sınıfındaki '.base()' isimli fonksiyon ise bir 'iterator' döndürmekte olup '.end()' // fonksiyonunun döndürdüğü konum bilgisini tutmaktadır. auto normalIter = reverseIter.base(); if(normalIter == iVec.end()) { // Fakat ilgili konumu 'derefence' ETMEMELİYİZ. 'Tanımsız Davranış' oluşturur. std::cout << "Son öğeden sonraki öğenin konumundayız. =>" << *normalIter << "\n"; } // '++' operatörünün operandı olduğunda içerideki reverse_iteratör bir azalmaktadır. ++reverseIter; // Artık 'reverseIter' direkt olarak son öğenin konum bilgisini tutmaktadır. // 'derefence' edildiğinde, bir önceki öğeyi döndürmektedir. std::cout << "Sondan bir önceki öğe : " << *reverseIter << "\n"; // Konum bilgisi olarak en son öğeden bir önceki öğenin konum bilgisini tutmaktadır. auto normalIterTwo = reverseIter.base(); std::cout << "Son öğe : " << *normalIterTwo << "\n"; /* # OUTPUT # 9 Son öğeden sonraki öğenin konumundayız. => 0 Sondan bir önceki öğe : 8 Son öğe : 9 */ // YANLIŞ KULLANILM std::cout << "reverseIter | normalIter\n"; while(reverseIter != iVec.rend()) std::cout << *reverseIter++ << " | " << *normalIterTwo++ << "\n"; /* # OUTPUT # reverseIter | normalIter 8 | 9 7 | 0 6 | 1041 5 | 0 4 | 545005620 3 | 808793141 2 | 875639609 1 | 1869482553 */ // Çıktıdan da görüldüğü üzere 'Tanımsız Davranış' meydana gelmiştir. Çünkü '.base()' sınıfı // 'iterator' döndürmektedir. // DOĞRU KULLANILM std::cout << "reverseIter | normalIter\n"; while(reverseIter != iVec.rend()) std::cout << *reverseIter++ << " | " << *normalIterTwo-- << "\n"; /* # OUTPUT # reverseIter | normalIter 8 | 9 7 | 8 6 | 7 5 | 6 4 | 5 3 | 4 2 | 3 1 | 2 */ // Dolayısla o iteratörü '--' operatörü ile birlikte kullanmamız gerekmektedir. } * Örnek 4, '.base()' fonksiyonunun kullanımına bir örnek: //.. template void print_range(InIter beg, InIter end) { while (beg != end) std::cout << *beg++ << ", "; std::cout << "\n"; } int main() { std::vector iVec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto reverseIter_begin = iVec.rbegin(); auto reverseIter_end = iVec.rend(); print_range(reverseIter_begin, reverseIter_end); // OUTPUT => 9, 8, 7, 6, 5, 4, 3, 2, 1, print_range(reverseIter_end.base(), reverseIter_begin.base()); // OUTPUT => 1, 2, 3, 4, 5, 6, 7, 8, 9, } * Örnek 5, Peki bu tip iteratör ile algoritmaları kombine edersek ne olur? : //.. int main() { /* # OUTPUT # I: 77 96 46 13 79 93 20 44 4 72 60 39 94 83 25 93 4 65 58 88 71 89 16 70 99 6 15 ----------------------------------------------------------------------------- II: 4 4 6 13 15 16 20 25 39 44 46 58 60 65 70 71 72 77 79 83 88 89 93 93 94 96 99 ----------------------------------------------------------------------------- III: 99 96 94 93 93 89 88 83 79 77 72 71 70 65 60 58 46 44 39 25 20 16 15 13 6 4 4 ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 27, Irand{1, 100}); pc(iVec); // I sort(iVec.begin(), iVec.end()); pc(iVec); // II sort(iVec.rbegin(), iVec.rend()); pc(iVec); // III } >>>> 'Iterator' şu aşağıdakilerden meydana gelmektedir. Bunlar, >>>>> 'iterator' : Bu tip iteratörler kapların '.begin()' ve '.end()' fonksiyonları ile elde edilirler. Ek olarak C++17 ile bu üye fonksiyonların global fonksiyon versiyonları da dile eklenmiştir. Sırasıyla 'begin()' ve 'end()' fonksiyonları, üye fonksiyonların eşlenikleridir. >>>>> 'const_iterator' : Bu tip iteratörler kapların '.cbegin()' ve '.cend()' fonksiyonları ile elde edilirler. Ek olarak C++17 ile bu üye fonksiyonların global fonksiyon versiyonları da dile eklenmiştir. Sırasıyla 'cbegin()' ve 'cend()' fonksiyonları, üye fonksiyonların eşlenikleridir. >>>>> 'reverse_iterator' : Konteynır sıfının 'nested-type' olan iteratör sınıfı en az 'bidirectional_iterator' grubuna dahil olmalı ki bu tip iteratör gruplarına sahip olsunlar. Bu tip iteratörler kapların '.rbegin()' ve '.rend()' fonksiyonları ile elde edilirler. Ek olarak C++17 ile bu üye fonksiyonların global fonksiyon versiyonları da dile eklenmiştir. Sırasıyla 'rbegin()' ve 'rend()' fonksiyonları, üye fonksiyonların eşlenikleridir. >>>>> 'const_reverse_iterator' : Konteynır sıfının 'nested-type' olan iteratör sınıfı en az 'bidirectional_iterator' grubuna dahil olmalı ki bu tip iteratör gruplarına sahip olsunlar. Bu tip iteratörler kapların '.crbegin()' ve '.crend()' fonksiyonları ile elde edilirler. Ek olarak C++17 ile bu üye fonksiyonların global fonksiyon versiyonları da dile eklenmiştir. Sırasıyla 'crbegin()' ve 'crend()' fonksiyonları, üye fonksiyonların eşlenikleridir. >>>> İteratörleri manipüle eden yardımcı fonksiyon şablonları: Bunlar, >>>>> 'advance()' : Bir iteratörü referans yoluyla alıyor ve bu iteratörü 'n' pozisyon ilerletmektedir. Argüman olarak geçilen iteratör 'bidirectional_iterator' ise negatif rakam da geçilebilir. Hiç bir şekilde bir 'exception' 'throw' ETMEMEKTEDİR. Dolayısıyla kaydırma yaparken 'range' dışına çıkılmadığından BİZ SORUMLUYUZ. Artık bu fonksiyona geçilen iteratörün tuttuğu konum bilgisi değişmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # [773] => 773 810 747 998 999 193 461 993 91 292 579 76 972 404 582 643 623 64 ----------------------------------------------------------------------------- [193] => 773 810 747 998 999 193 461 993 91 292 579 76 972 404 582 643 623 64 ----------------------------------------------------------------------------- [747] => 773 810 747 998 999 193 461 993 91 292 579 76 972 404 582 643 623 64 ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 18, Irand{0, 1000}); auto iter = iVec.begin(); std::cout << "[" << *iter << "] => "; pc(iVec); // iter += 5; // İlgili iteratörün en az 'random_access_iterator' olması gerekmektedir. std::advance(iter, 5); std::cout << "[" << *iter << "] => "; pc(iVec); // iter -= 3; // İlgili iteratörün en az 'random_access_iterator' olması gerekmektedir. std::advance(iter, -3); // İlgili iteratörün en az 'bidirectional_iterator' olması gerekmektedir. std::cout << "[" << *iter << "] => "; pc(iVec); } * Örnek 2, //.. int main() { /* # OUTPUT # [701] => 701 716 600 69 24 477 902 71 211 998 168 915 411 768 241 135 679 771 ----------------------------------------------------------------------------- [477] => 701 716 600 69 24 477 902 71 211 998 168 915 411 768 241 135 679 771 ----------------------------------------------------------------------------- [600] => 701 716 600 69 24 477 902 71 211 998 168 915 411 768 241 135 679 771 ----------------------------------------------------------------------------- */ std::list iList; fc(iList, 18, Irand{0, 1000}); auto iter = iList.begin(); std::cout << "[" << *iter << "] => "; pc(iList); // iter += 5; // İlgili iteratörün en az 'random_access_iterator' olması gerekmektedir. std::advance(iter, 5); // Fakat bu fonksiyon çağrısı ile ilgili iteratörü kaydırabiliyoruz. std::cout << "[" << *iter << "] => "; pc(iList); // iter -= 3; // İlgili iteratörün en az 'random_access_iterator' olması gerekmektedir. std::advance(iter, -3); // Fakat bu fonksiyon çağrısı ile ilgili iteratörü kaydırabiliyoruz. std::cout << "[" << *iter << "] => "; pc(iList); } >>>>>> Yukarıdaki her iki örnekte de görüldüğü üzere bir kod seçimi yapılmaktadır. İlgili fonksiyiona geçilen argüman bir 'vektor' sınıf türünden ise ilgili sınıfın '.operator+=()' fonksiyonuna çağrı yapılıyor, fakat ilgili argüman bir 'list' sınıf türünden ise bir döngü içerisinde 'n' kez ilgili iteratörün arttırılması/azaltılması gerekmektedir. Peki arka planda bunu mümkün kılan yapılanma nasıldır? * Örnek 1, Meta programlamada kullanılan 'tag-dispatch' tekniği: (diğer teknikler ise 'constexpr if' ve 'spayn-en') //.. // 'std::random_access_iterator_tag' için 'overload' : template void MyAdvanceImplementation(Iter& iter, int n, std::random_access_iterator_tag) { iter += n; } // 'std::bidirectional_iterator_tag' için 'overload' : template void MyAdvanceImplementation(Iter& iter, int n, std::bidirectional_iterator_tag) { if( n > 0) { while(n--) ++iter; } else { while(n++) --iter; } } //.. Diğer 'tag' için 'overload' lar. template void MyAdvance(Iter& iter, int n) { MyAdvanceImplementation(iter, n, typename Iter::iterator_category{}); // Eğer 'Iter' yerine 'std::vector::iterator' gelirse, 'Iter::iterator_category' yerine // 'random_access_iterator_tag' gelecektir. // Eğer 'Iter' yerine 'std::list::iterator' gelirse, 'Iter::iterator_category' yerine // 'bidirectional_iterator_tag' gelecektir. // Bunu sağlamak için de 'typename' anahtar SÖZCÜĞÜNÜ KULLANMAK ZORUNLU. Tıpkı yukarda daha önce // 'BackInsertIterator' sınıf şablonunu yazarken ilgili kabın tuttuğu elemanların tür bilgisini // elde ederkenki gibi. // Biz, karşılık gelen tür ne ise o türden bir geçici nesne oluşturuyoruz. Böylelikle, bu üçüncü // argümana bakarak, yukarıdaki 'MyAdvanceImplementation' fonksiyonlarından hangi 'overload' // versiyon uygun ise o seçilecektir. } int main() { /* # OUTPUT # [9] => 9 90 94 24 57 44 24 41 31 46 83 52 11 96 15 17 51 83 94 70 88 76 80 22 47 ----------------------------------------------------------------------------- [31] => 9 90 94 24 57 44 24 41 31 46 83 52 11 96 15 17 51 83 94 70 88 76 80 22 47 ----------------------------------------------------------------------------- [94] => 9 90 94 24 57 44 24 41 31 46 83 52 11 96 15 17 51 83 94 70 88 76 80 22 47 ----------------------------------------------------------------------------- */ std::list iList; fc(iList, 25, Irand{9, 100}); auto iter = iList.begin(); std::cout << "[" << *iter << "] => "; pc(iList); MyAdvance(iter, 8); std::cout << "[" << *iter << "] => "; pc(iList); MyAdvance(iter, -6); std::cout << "[" << *iter << "] => "; pc(iList); // BU MEKANİZMANIN ÇALIŞMA ZAMANI İLE BİR ALAKASI YOKTUR. TAMAMİYLE DERLEME ZAMANINA İLİŞKİNDİR. } >>>>> 'distance()' : İki iteratör arasındaki mesafeyi elde edebiliyoruz. * Örnek 1, //.. int main() { /* # OUTPUT # 1528726442 463433013 208427435 482200417 1950432305 444597464 514460685 1709010121 1008062565 1921019392 ----------------------------------------------------------------------------- size : 10 size : 10 Aranacak deger : 1008062565 Aranan [1008062565] rakamı [8] indisinde bulundu. Aranan [1008062565] rakamı [8] indisinde bulundu. */ randomize(); std::vector iVec; fc(iVec, 10, random); pc(iVec); std::cout << "size : " << std::distance(iVec.begin(), iVec.end()) << "\n"; std::cout << "size : " << iVec.end() - iVec.begin() << "\n"; // En az 'random_access_iterator' olmalı. std::cout << "Aranacak deger : "; int numberToLookUp; std::cin >> numberToLookUp; if( auto iter = std::find(iVec.begin(), iVec.end(), numberToLookUp); iter != iVec.end() ) { std::cout << "Aranan [" << numberToLookUp << "] rakamı [" << std::distance(iVec.begin(), iter) << "] indisinde bulundu.\n"; // En az 'random_access_iterator' olmalı. std::cout << "Aranan [" << numberToLookUp << "] rakamı [" << iter - iVec.begin() << "] indisinde bulundu.\n"; } else { std::cout << "Aranan [" << numberToLookUp << "] rakamı BULUNAMADI.\n"; } } * Örnek 2, //.. int main() { /* # OUTPUT # 1694615831 1506296853 50781890 579410133 727108356 1768694123 945782193 903807668 1221500803 1885210938 ----------------------------------------------------------------------------- size : 10 Aranacak deger : 1694615831 Aranan [1694615831] rakamı [0] indisinde bulundu. */ randomize(); std::list iList; fc(iList, 10, random); pc(iList); std::cout << "size : " << std::distance(iList.begin(), iList.end()) << "\n"; // std::cout << "size : " << iList.end() - iList.begin() << "\n"; // En az 'random_access_iterator' olmalı. std::cout << "Aranacak deger : "; int numberToLookUp; std::cin >> numberToLookUp; if( auto iter = std::find(iList.begin(), iList.end(), numberToLookUp); iter != iList.end() ) { std::cout << "Aranan [" << numberToLookUp << "] rakamı [" << std::distance(iList.begin(), iter) << "] indisinde bulundu.\n"; // std::cout << "Aranan [" << numberToLookUp // << "] rakamı [" << iter - iList.begin() << "] indisinde bulundu.\n"; // En az 'random_access_iterator' olmalı. } else { std::cout << "Aranan [" << numberToLookUp << "] rakamı BULUNAMADI.\n"; } } >>>>> C++11 ile dile eklenen 'next()' ve 'prev()' fonksiyonları : İteratörün tuttuğu konum bilgisini değiştirmeden sadece o konumdan 'n' konum sonrası veya öncesindeki konumlara erişmek için kullanılır. Her iki fonksion da ikinci argümanı varsayılan olarak bir değerini almaktadır. * Örnek 1, //.. int main() { /* # OUTPUT # 1586070685 1978505654 1266866527 300525574 1979937931 1320568275 966617558 2132587798 1755296543 739439990 ----------------------------------------------------------------------------- Birinci öğe : 1586070685 [3] konum sonrasındaki öğe : 300525574 Birinci öğe : 1586070685 İkinci öğe : 1978505654 */ randomize(); std::vector iVec; fc(iVec, 10, random); pc(iVec); auto iter = iVec.begin(); std::cout << "Birinci öğe : " << *iter << "\n"; std::cout << "[" << 3 << "] konum sonrasındaki öğe : " << *next(iter, 3) << "\n"; std::cout << "Birinci öğe : " << *iter << "\n"; std::cout << "İkinci öğe : " << *next(iter) << "\n"; } * Örnek 2, //.. int main() { /* # OUTPUT # 863294851 1766641905 430546786 197891584 504445422 1476846807 905168151 580938961 643899547 1618256969 ----------------------------------------------------------------------------- En sonki oge sonraki oge : 0 En sonki oge : 1618256969 Birinci oge : 863294851 */ randomize(); std::vector iVec; fc(iVec, 10, random); pc(iVec); auto iter = iVec.end(); // 'iter' konumunda bir öğe yok. Çünkü son öğenin konumundan bir sonraki konumu göstermektedir. // Dolayısla 'derefence' etmem durumunda 'Tanımsız Davranış' oluşacaktı. std::cout << "En sonki oge sonraki oge : " << *iter << "\n"; // Tanımsız Davranış. std::cout << "En sonki oge : " << *prev(iter) << "\n"; std::cout << "Birinci oge : " << *prev(iter, 10) << "\n"; } * Örnek 3, //.. int main() { /* # OUTPUT # kirikkale diyarbakir eskisehir osmaniye kastamonu cankiri siirt malatya karaman kirikkale ----------------------------------------------------------------------------- kirikkale diyarbakir eskisehir osmaniye kastamonu cankiri siirt malatya karaman kirikkale ----------------------------------------------------------------------------- diyarbakir eskisehir osmaniye kastamonu cankiri siirt malatya karaman ----------------------------------------------------------------------------- */ std::list iList; fc(iList, 10, rtown); pc(iList); pr(iList.begin(), iList.end()); // Belli bir aralıktaki öğeleri yazdıran fonksiyonumuz. pr ( next(iList.begin()), prev(iList.end()) ); // İlk öğe hariç, son öğe haric, aradaki bütün öğeleri yazdırdık. } >>>>> 'iter_swap' : İki iteratör konumundaki öğeleri takas etmektedir. Dolayısıyla bu öğelerin birbirine ATANABİLİR olması gerekiyor. İstemeden de olsak VERİ KAYBINA neden olabiliriz. Nesneleri birbirine atadığımız için bu fonksiyona geçilen iteratörlerin de kategorileri aynı olması GEREKMİYOR. * Örnek 1, //.. // 'iter_swap' fonksiyonunun temsili implementasyonu: template void MyIterSwap(IterOne first, IterTwo second) { auto temp = *first; *first = *second; *second = temp; } int main() { /* # OUTPUT # batman kirikkale bingol amasya antalya amasya duzce kastamonu duzce nigde ----------------------------------------------------------------------------- eskisehir bitlis aksaray hakkari bolu denizli canakkale kilis sinop canakkale ----------------------------------------------------------------------------- ***************************************************************************** eskisehir kirikkale bingol amasya antalya amasya duzce kastamonu duzce nigde ----------------------------------------------------------------------------- batman bitlis aksaray hakkari bolu denizli canakkale kilis sinop canakkale ----------------------------------------------------------------------------- ***************************************************************************** eskisehir canakkale bingol amasya antalya amasya duzce kastamonu duzce nigde ----------------------------------------------------------------------------- batman bitlis aksaray hakkari bolu denizli canakkale kilis sinop kirikkale ----------------------------------------------------------------------------- */ std::list iList; fc(iList, 10, rtown); std::vector iVec; fc(iVec, 10, rtown); pc(iList); pc(iVec); std::cout << "\n*****************************************************************************\n\n"; MyIterSwap(iList.begin(), iVec.begin()); pc(iList); pc(iVec); std::cout << "\n*****************************************************************************\n\n"; iter_swap(next(iList.begin()), prev(iVec.end())); pc(iList); pc(iVec); } * Örnek 2, //.. int main() { /* # OUTPUT # */ randomize(); std::vector iVec; fc(iVec, 10, random); pc(iVec); std::list iList; fc(iList, 10, random); pc(iList); std::iter_swap ( next(iVec.begin(), 5), prev(iList.end(), 5) ); // ERROR: => The dereferenced values *a and *b must be "swappable", which implies that swap(*a, *b) // must be valid, and thus the dereferenced types must be identical, although the iterator types do // not have to be. pc(iVec); pc(iList); } >>>> 'iterator invalidation' : Aşağıdaki açıklamaları inceleyelim. >>>>> '.insert()', '.emplace_back()', '.emplace()' ve '.push_back()' fonksiyonları 'reallocation' a sebebiyet verebilir. Bu durumda bütün 'references', 'pointers' ve 'iterators' geçersiz hale gelirler. Çünkü yeni bir bellek alanı elde edildiğinden dolayı önceki bellek alanı geri verilmektedir. Eğer iş bu fonksiyonlar 'reallocation' a sebebiyet vermez ise ekleme yapılan noktadan önceki öğeleri gösteren 'references', 'pointers' ve 'iterators' GEÇERLİ OLMAYA DEVAM EDERLER. İş bu noktadan sonraki öğeleri gösterenler ise GEÇERSİZ OLACAKLARDIR. >>>>> '.reserve()' fonksiyonu velev ki 'reallocation' a neden olursa yine bütün 'references', 'pointers' ve 'iterators' geçersiz hale gelecektir. Fakat 'reserve' işleminden sonra yapılan eklemeler, tekrardan 'reallocation' tetiklenmediği sürece, ilgili referansları, iteratörleri ve göstericileri GEÇERSİZ HALE GETİRMEYECEKTİR. Çünkü bunlar zaten '.reserve()' fonksiyonundan önce konum gösteriyorlar. 'reallocation' da gerçekleşmedi. Dolayısıyla adres bilgileri değişmedi. >>>>> '.erase()' ve '.pop_back()' fonksiyonları silme işleminin yapıldığı konumu ve sonrasındaki konumu gösteren 'references', 'pointers' ve 'iterators' GEÇERSİZ HALE GETİRECEKTİR. >>> 'Algorithms' grubu ise fonksiyon şablonlarıdır. Kaplar üzerinde iş yapacak algoritmaları implemente etmektedirler. Parametreleri iteratör şeklindedirler. >>>> 'Copy' algoritması : En sık kullanılan algoritmalardan bir tanesidir. Bir 'range' içerisindeki öğeleri başka bir 'range' içerisine kopyalamaktadır. Gelin temsili bir implementasyonuna bakalım. * Örnek 1, //.. template OutIter MyCopy(InIter beg, InIter end, OutIter destbeg) { while(beg != end) *destbeg++ = *beg++; return destbeg; } // 'InIter' ismine karşılık gelecek tür her neyse aşağıdaki işlemleri desteklemesi gerekmektedir; // 'operator!=' fonksiyonuna operand olabilme, // 'operator*' fonksiyonuna operand olabilme, ki burada 'derefence' için kullanılmaktadır, // 'operator++(int)' fonksiyonuna operand olabilme. // Yukarıdaki tablodan baktığımız zaman bu operatörlere operand olabilen minimum iteratör grubu // 'input_iterator' grubudur. İşte bu neden dolayı, kullanıcıya da mesaj verebilmek adına, 'input_iterator' // manasına gelen 'InIter' ismi KULLANILMIŞTIR. Benzer neden dolayı 'OutIter' ismine karşılık gelen minimum // iteratör grubu 'output_iterator' olduğundan, bu şekilde bir isim verilmiştir. İSİMLENDİRME KONVENSİYONU // BU ŞEKİLDEDİR. // Yine yukarıdaki fonksiyonun geri dönüş değer konum bilgisi olduğu için, bu fonksiyona geçilen üçüncü // argüman ile birlikte kullanılması durumunda, bir 'range' oluşturulabilir. * Örnek 2, //.. int main() { std::vector iVec{ 1, 2, 3, 4, 5, 6 }; std::vector vx; copy(iVec.begin(), iVec.end(), vx.begin()); // Bu fonksiyon çağrısı 'Tanımsız Davranış' a neden olacaktır. Çünkü 'vx' boş bir kap. Usüle uygun // çalışması için en az 6 elemana sahip olması gerekiyordu. Velev ki 'vx' üç elemanlı olsaydı bile yine // çalışma zamanında hata alacaktık. Peki bu problemleme alternatif çözümler nelerdir? // El-cevap: aşağıdaki örneği inceleyelim. // UNUTULMAMALIDIR Kİ BOŞ BİR KAPTA '.end()' ve '.begin()' fonksiyonları aynı konum bilgisi // döndürmektedir. İş bu konum bilgileri, ilgili kap boş olduğundan, 'derefence' EDİLMEMELİDİR. } * Örnek 3, //.. // Bu algoritmadan derleyiciye öyle bir fonksiyon kodu yazdıracağım ki kaynak aralıktaki öğeler, bir kaba, // kabın '.push_back()' fonksiyonu ile 'insert' edileceklerdir. template OutIter MyCopy(InIter beg, InIter end, OutIter destBeg) { while (beg != end) *destBeg++ = *beg++; return destBeg; } // Öyle bir sınıf şablonu oluşturacağız ki 'OutIter' yerine bizimki kullanılacak. template class BackInsertIterator{ public: // Artık bu sınıf türünden bir nesne hayata geldiğide veri elemanı olan 'm_r', 'Ctor.' fonksiyonuna // geçilen argümana referans olacaktır. BackInsertIterator(C& other) : m_r{other} {} BackInsertIterator& operator*() { return *this; } // Bu fonksiyona yapılan çağrı, bu fonksiyonu çağıran nesnenin kendisini döndürmektedir. Örneğin, // '*destBeg' demek aslında 'destBeg' nesnesinin kendisi // demektir. BackInsertIterator& operator++() { return *this; } // Bu fonksiyona yapılan çağrı, bu fonksiyonu çağıran nesnenin kendisini döndürmektedir. Örneğin, // '++destBeg' demek aslında 'destBeg' nesnesinin kendisi demektir. BackInsertIterator& operator++(int) { return *this; } // Bu fonksiyona yapılan çağrı, bu fonksiyonu çağıran nesnenin kendisini döndürmektedir. Örneğin, // 'destBeg++' demek aslında 'destBeg' nesnesinin kendisi demektir. BackInsertIterator& operator=( const typename C::value_type& value ) // Bu fonksiyonun parametresi olan '::value_type', kapta tutulan öğelerin tür bilgisini // döndürmektedir. Aşağıdaki kullanımı örnek alırsak 'C::value_type' yerine 'int' gelecektir. Buradan // da hareketle 'value' isimli değişkenin türü 'const int&' olacaktır. Kullanım şeklinin de parantez // içerisindeki gibi olduğunu UNUTMAYALIM. Önce 'const' niteleyicisi, sonrasında da 'typename' // anahtar sözcüğü ve son olarak da 'C::value_type' niteleyicisi. En son olarak da '&' deklaratörü. { // 'm_r' isimli veri elemanımız, elemanları 'int' türden olan bir kaba referans olduğundan, // bu çağrı da legal hale gelmiştir. m_r.push_back(value); // Bu fonksiyona yapılan çağrı, bu fonksiyonu çağıran nesnenin kendisini döndürmektedir. return *this; } private: C& m_r; // Artık 'C' her ne tür ise 'm_r' ise o türden bir referans. }; // Öyle bir fonksiyon şablonu yazacağız ki yukarıdaki 'BackInsertIterator' sınıf türünü döndüreceğiz. // Bu fonksiyon şablonundan yazılan fonksiyona geçilen argümanın türü neyse, 'other' isimli değişken de o // türden bir referans olacaktır. Aşağıdaki örneği baz alırsak; 'other' değişkeninin türü 'std::vector' // türüne referanstır. Yani 'T' yerine 'std::vector' gelmektedir. template BackInsertIterator BackInserter(T& other) { return BackInsertIterator{ other }; // 'BackInsertIterator' sınıfının veri elemanı olan 'm_r' ise bu çağrı sonucunda 'std::vector' // türüne referans hale gelmiştir. } int main() { /* # OUTPUT # 2 4 6 8 10 ----------------------------------------------------------------------------- 2 4 6 8 10 ----------------------------------------------------------------------------- 2 4 6 8 10 ----------------------------------------------------------------------------- */ std::vector iVec{ 2, 4, 6, 8, 10 }; std::vector vx; BackInsertIterator> myCustomIter(vx); // Tür çıkarımı sonucunda sınıf şablonu parametresi olan 'C' yerine 'std::vector>' gelecektir ve // derleyicinin yazacağı sınıfının veri elemanı olan 'm_r' artık 'vx' öğesine referans haline gelecektir. // Yani 'm_r' demek 'vx' demektir. MyCopy(iVec.begin(), iVec.end(), myCustomIter); // Üçüncü argüman olan sınıf '.operator*()', '.operator++()' ve 'operator==()' fonksiyonlarını 'overload' // etmelidir ki sentaks hatası almayalım. // Artık hedefteki kaba sondan ekleme yaparak bir kopyalama işlemi gerçekleştirildi. pc(vx); // İsimlendirilmiş bir sınıf nesnesi yerine geçici nesne de oluşturabilirdik; std::vector vxx; MyCopy(iVec.begin(), iVec.end(), BackInsertIterator>{ vxx }); pc(vxx); // Her ne kadar geçici nesne kullansak da yazım uzunluğundan dolayı, bunu bir fonksiyon çağrısı ile de // yaptırabiliriz: std::vector vxxx; MyCopy(iVec.begin(), iVec.end(), BackInserter(vxxx)); pc(vxxx); // PEKİ 'STL' İÇERİSİNDE YUKARIDAKİ MEKANİZMAYI KULLANAN MEKANİZMA VAR MIDIR? // El-cevap : Aşağıdaki örneği inceleyelim. } * Örnek 4, 'back_inserter' iteratör sınıfının kullanılması: //.. int main() { /* # OUTPUT # ----------------------------------------------------------------------------- 2 4 6 8 10 ----------------------------------------------------------------------------- */ std::vector iVec{ 2, 4, 6, 8, 10 }; std::vector vx; pc(vx); copy(iVec.begin(), iVec.end(), std::back_inserter(vx)); pc(vx); // Artık 'back_inserter' fonksiyonunun arka planda yaptıklarını da görmüş olduk. Yukarıdaki kullanım ile // boş bir kaba da yazma işlemi yapabiliyoruz. Bütün yazma algoritmaları için bunu kullanabiliriz. // UNUTULMAMALIDIR Kİ 'vx' KABINA SONDAN EKLEME YAPILMAKTADIR. } * Örnek 5, 'front_inserter' iteratör sınıfının kullanılması: //.. int main() { /* # OUTPUT # I: ----------------------------------------------------------------------------- II: 2 4 6 8 10 ----------------------------------------------------------------------------- III: 10 8 6 4 2 2 4 6 8 10 ----------------------------------------------------------------------------- */ std::vector iVec{ 2, 4, 6, 8, 10 }; std::list iList; pc(iList); // I copy(iVec.begin(), iVec.end(), back_inserter(iList)); // İlgili listeye sondan ekleme yapıldı. pc(iList); // II copy(iVec.begin(), iVec.end(), front_inserter(iList)); // İlgili listeye baştan ekleme yapıldı. pc(iList); // III } * Örnek 6, 'back_inserter' ve 'front_inserter' sınıflarının birlikte kullanılması ve 'overwrite' durumu: //.. int main() { /* # OUTPUT # I: ----------------------------------------------------------------------------- II: 2 4 6 8 10 ----------------------------------------------------------------------------- III: 10 8 6 4 2 2 4 6 8 10 ----------------------------------------------------------------------------- IIII: 20 40 60 80 100 2 4 6 8 10 ----------------------------------------------------------------------------- */ std::vector iVecTwo{ 20, 40, 60, 80, 100 }; std::vector iVec{ 2, 4, 6, 8, 10 }; std::list iList; pc(iList); // I copy(iVec.begin(), iVec.end(), back_inserter(iList)); // İlgili listeye sondan ekleme yapıldı. pc(iList); // II // İlgili listeye baştan ekleme yapıldı. copy(iVec.begin(), iVec.end(), front_inserter(iList)); pc(iList); // III // İlgili listenin başındaki öğeler 'overwrite' edildi. copy(iVecTwo.begin(), iVecTwo.end(), iList.begin()); pc(iList); // IIII } >>>> 'Count' algoritmasıdır: Bir 'range' içerisindeki öğeleri saymaktadır. Gelin temsili bir implementasyonuna bakalım. * Örnek 1, //.. template int MyCount(InIter beg, InIter end, const T& t) { int cnt{}; while(beg != end) { if( *beg == t) ++cnt; ++beg; } return cnt; } * Örnek 2, //.. int main() { std::vector svec; fc(svec, 100000, rname); // İlgili 'svec' isimli kap, 'rname' isimli callable vasıtasıyla 100'000 adet öğeye sahip olmuştur. std::string nameToLookUp = "ayse"; std::cout << count(begin(svec), end(svec), nameToLookUp) << " adet " << nameToLookUp << "bulundu.\n"; } * Örnek 3, //.. int main() { std::list myList; // İlgili 'myList' isimli kap, 'Date::random' isimli callable vasıtasıyla 1'000'000 adet öğeye sahip // olmuştur. fc(myList, 1000000, Date::random); Date dateToLookUp{10, 12, 1999}; std::cout << count( myList.begin(), myList.end(), dateToLookUp ) << " adet " << dateToLookUp << "bulundu.\n"; } >>>> 'find' algoritması (Mülakat sorusu) : Bir 'linear-search' algoritmasıdır. Arama için kullanılır. * Örnek 1, Bir kap içerisindeki belirli bir değere sahip son öğeyi sil. //.. int main() { /* # OUTPUT # I: 1 2 5 9 6 5 7 8 2 3 4 82 2 3 ----------------------------------------------------------------------------- II: 2 5 9 6 5 7 8 2 3 4 82 2 3 ----------------------------------------------------------------------------- III: 2 5 9 6 5 7 8 2 3 4 82 2 ----------------------------------------------------------------------------- IIII: 2 5 9 6 5 7 8 2 3 4 82 ----------------------------------------------------------------------------- */ std::vector iVec{ 1, 2, 5, 9, 6, 5, 7, 8, 2, 3, 4, 82, 2, 3 }; pc(iVec); // I // '.erase()' fonksiyonu bir konum bilgisi almakta ve o konumdaki öğeyi silmektedir. Dolayısıyla // kaptaki ilk öğe silinmiş olacaktır. iVec.erase(iVec.begin()); pc(iVec); // II iVec.erase(iVec.end() - 1); // Kaptaki son öğe silinecektir. pc(iVec); // III // Sorunun çözümü; // Aranacak öğe. int ival = 2; // Kaptaki ilgili değere eşit son öğenin konum bilgisi döndürülmüş oldu. auto iter = find(iVec.rbegin(), iVec.rend(), ival); // Bir değerini çıkartıyoruz. Çünkü 'iter' konum olarak aslında '3' rakamının konum bilgisini // tutmakta çünkü 'reverse_iterator' kullandık. Bir çıkartarak ondan da bir önceki konum bilgisine // erişmiş oluyoruz. iVec.erase(iter.base() - 1); pc(iVec); // IIII } >>>> Sonu '_if' ile biten algoritmalar argüman olarak bir 'predicate' alıyorlar. Kaptaki öğeler üzerinde işlem yapıyorlar eğer 'predicate' 'true' döndürürse. Gelin temsili bir implementasyonuna bakalım. * Örnek 1, 'Count_if' : //.. template int Count_if(InIter beg, InIter end, Pred myPredicate) { int cnt{}; while( beg != end ){ if( myPredicate(*beg) ) ++cnt; ++beg; } return cnt; } * Örnek 2, //.. bool is_ok(const Date& date) { return date.month_day() > 25; } int main() { std::vector myVec; fc(myVec, 100000, Date::random); // İlgili 'myVec' isimli kap, 'Date::random' isimli callable vasıtasıyla 100'000 adet öğeye sahiptir. std::cout << count_if(myVec.begin(), myVec.end(), is_ok) << "\n"; // İş bu kaptaki öğelerden 'gün' bilgisinin 25'ten büyük olanları sayılmıştır. } * Örnek 3, bool has_length_five(const std::string& s) { return s.length() == 5; } int main() { std::vector myVec; fc(myVec, 10000, rname); // İlgili 'myVec' isimli kap, 'rname' isimli callable vesilesiyle 10000 adet öğeye sahip olmuştur. std::cout << count_if( myVec.begin(), myVec.end(), has_length_five ) << " adet öğenin uzunluğu beşe eşittir.\n"; // OUTPUT => 3910 adet öğenin uzunluğu beşe eşittir. } * Örnek 4, 'Copy_if' : 'UnPred' denmesinin sebebi 'UnaryPredicate' manasında. Yani tek bir argüman alan 'predicate' demek için. //.. class DivPred{ public: DivPred(int value) : m_val{value}{} bool operator()(int value)const { return value % m_val == 0; } private: int m_val; }; int main() { std::vector iVec; std::vector iList(100); fc(iVec, 100, Irand(0, 10000)); auto iter_end = std::copy_if(iVec.begin(), iVec.end(), iList.begin(), DivPred(5)); for(auto iter = iList.begin(); iter != iter_end; ++iter) std::cout << *iter << ", "; // OUTPUT => 9945, 3975, 4940, 7880, 2630, 325, 8295, 6245, 3110, 8525, 1010, 3590, 7820, 2690, 9300, // 100, 755, 2410, 2195, 4530, 9930, pr(iList.begin(), iter_end); // OUTPUT => 6585 7300 8765 4465 8400 9370 8215 8040 6085 9055 4520 245 3410 3025 5810 9995 6265 9705 // ----------------------------------------------------------------------------- } * Örnek 5, 'find_if' : Bir 'range' içerisinde bir koşulu sağlayan ilk öğeyi ARAMAKTADIR. //.. template InIter Find_If(InIter beg, InIter end, UnPred myPredicate) { while (beg != end) if(myPredicate(*beg)) { ++beg; return beg; } return beg; } class CharPred{ public: CharPred(char c) : m_c{c} {} bool operator()(const std::string& name) { return name.find(m_c) != std::string::npos; } private: char m_c; }; int main() { /* # OUTPUT # emrecan pakize sumeyye emine mahir sezer tekin feramuz nazli melike mukerrem nefes zerrin sumeyye yavuz nalan kasim hulki devlet garo bekir beste yavuz abdullah selenay ----------------------------------------------------------------------------- Aranan {e} karakteri, {emrecan} isminde bulundu. Aranan [q] karakteri bulunamadı. Aranan {k} karakteri, {pakize} isminde bulundu. */ std::vector sVec; fc(sVec, 25, rname); pc(sVec); char charToLookUp = 'e'; if(auto iter = Find_If(sVec.begin(), sVec.end(), CharPred{charToLookUp}); iter == sVec.end()) std::cout << "Aranan [" << charToLookUp << "] karakteri bulunamadı.\n"; else std::cout << "Aranan {" << charToLookUp << "} karakteri, {" << *iter << "} isminde bulundu.\n"; charToLookUp = 'q'; if(auto iter = std::find_if(sVec.begin(), sVec.end(), CharPred{charToLookUp}); iter == sVec.end()) std::cout << "Aranan [" << charToLookUp << "] karakteri bulunamadı.\n"; else std::cout << "Aranan {" << charToLookUp << "} karakteri, {" << *iter << "} isminde bulundu.\n"; charToLookUp = 'k'; if(auto iter = std::find_if( sVec.begin(), sVec.end(), [charToLookUp](const std::string& name){ return name.find(charToLookUp) != std::string::npos; } ); iter == sVec.end()) std::cout << "Aranan [" << charToLookUp << "] karakteri bulunamadı.\n"; else std::cout << "Aranan {" << charToLookUp << "} karakteri, {" << *iter << "} isminde bulundu.\n"; } >>>> 'reverse' algoritması: Bir 'range' içerisindeki öğeleri tersine çevirmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # 298407 73548 217062 633124 513747 379080 435106 340988 245473 549216 643147 363300 302239 66470 987288 873964 768419 226376 111238 376691 325296 935402 153142 731074 286564 ----------------------------------------------------------------------------- 286564 731074 153142 935402 325296 376691 111238 226376 768419 873964 987288 66470 302239 363300 643147 549216 245473 340988 435106 379080 513747 633124 217062 73548 298407 ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 25, Irand(0, 1000000)); pc(iVec); reverse(iVec.begin(), iVec.end()); pc(iVec); } * Örnek 2, //.. int main() { /* # OUTPUT # bilge aslican suleyman samet tunc ----------------------------------------------------------------------------- cnut temas namyelus nacilsa eglib ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 5, rname); pc(sVec); reverse(begin(sVec), end(sVec)); for(auto& name : sVec) reverse(begin(name), end(name)); pc(sVec); } Şimdi de 'STL' içerisindeki silme algoritmalarına değinelim. 'STL' bünyesindeki bir algoritma SİLMA İŞLEMİ ve EKLEME İŞLEMLERİNİ YAPAMAZ. Çünkü bu işlemleri yapan kapların kendi üye fonksiyonlarıdır. İlgili algoritmalar argüman olarak ilgili kapları almadıklarından, nasıl o kapların üye fonksiyonlarına erişebilirler? Peki buradaki silme işleminden kastedilen nedir? El-cevap: Bahsedilen algoritmalar LOJİK SİLME işlemi yapmaktadır, gerçek silme işlemi y apmamaktadır. Bu fonksiyonlar ise şunlardır: >>>> 'remove()' fonksiyonu: Aşağıdaki örneği inceleyelim. * Örnek 1, //.. int main() { /* # OUTPUT # ivec : [18] => 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 ivec : [18] => 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 2 1 */ std::vector iVec{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; std::cout << "ivec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; auto logic_end = std::remove(iVec.begin(), iVec.end(), 9); std::cout << "ivec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; // Çıktıtan da görüldüğü üzere ilgili kabın büyüklüğü değişmedi. Derleyici burada '9' rakamını kaptan // içerisinden ayıklar ve bu rakamın sağındaki rakamları da sola doğru kaydırdı. Yukarıdaki kullanım için iki // defa öteleme işlemi gerçekleştirildi. Bu işlemler yapılırken sağ taraftan da iki rakam daha eklendi fakat // bu iki rakamın ne olduğu bizim için ÖNEMLİ DEĞİL. Bu rakamlar '9' rakamı da olabilir başka rakamlar da. // 'remove()' fonksiyonu ise kaydırılan en sonki öğeden bir sonraki öğenin konumunu tutan bir iteratör // döndürmekte. Bir diğer değişle sağdan giren iki rakamdan soldaki rakamın konumu geri döndürüldü. Bu // konum bilgisine ise 'logic_end' konum bilgisi denmektedir. Eğer bizler bu 'logic_end' ve '.end()' // fonksiyonunun geri döndürdüğü iteratörleri, vektör sınıfının gerçekten de silme işlemi yapan üye // fonksiyonuna geçersek, gerçekten de silme işlemi gerçekleşecektir. /* # OUTPUT # ----------------------------------------------------------------------------- ivec : [16] => 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 ----------------------------------------------------------------------------- ivec : [2] => 2 1 ----------------------------------------------------------------------------- */ std::cout << "-----------------------------------------------------------------------------\n"; std::cout << "ivec : [" << std::distance(iVec.begin(), logic_end) << "] => "; pr(iVec.begin(), logic_end); // 'remove' fonksiyonu sonrasında gerçek rakamlar. std::cout << "ivec : [" << std::distance(logic_end, iVec.end()) << "] => "; pr(logic_end, iVec.end()); // 'remove' fonksiyonu sonrasında yeni eklenen önemsiz rakamlar. // Çıktıtan da görüldüğü üzere 'logic_end' konumu silinmemiş son öğeden sonraki öğe veya silinmiş ilk öğenin // konumu. Buradaki silme kelimesinden de kastedilen ilgili değişkenin 'range' içerisinden çıkartılmasıdır. /* # OUTPUT # ivec : [16] => 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 ----------------------------------------------------------------------------- */ iVec.erase(logic_end, iVec.end()); std::cout << "ivec : [" << iVec.size() << "] => "; pr(iVec.begin(), iVec.end()); // Gerçek silme işlemi sonucunda kaptaki rakamlar. // İşte şimdi gerçekten de silme işlemi gerçekleştirildi. } >>>> 'remove-erase' idiom: Aşağıdaki örneği inceleyelim. * Örnek 1, //.. Varsayalım ki bu şekilde bir eleman kümemiz olsun => { 2, 3, 5, 2, 6, 7, 2, 8, 2, 9, 1, 2, 3 } '.begin()' => ^ '.end()' => ^ 'remove()' fonksiyonu ile '2' rakamlarını silelim => { 3, 5, 6, 7, 8, 9, 1, 3, *, *, *, *, * } '.begin()' => ^ 'logic_end' => ^ '.end()' => ^ '.erase()' fonksiyonu ile gerçek silme yapalım => { 3, 5, 6, 7, 8, 9, 1, 3 } '.begin()' => ^ '.end()' => ^ * Örnek 2, Yukarıdaki gösterimi C++ kodu ile de gösterelim. //.. int main() { /* # OUTPUT # iVec : [50] => 10 18 17 15 17 12 12 10 20 14 4 5 7 3 14 9 20 5 17 6 4 6 3 19 4 13 0 10 7 2 8 14 8 10 14 11 7 11 7 10 13 9 10 5 14 16 14 0 18 14 iVec : [49] => 10 18 17 17 12 12 10 20 14 4 5 7 3 14 9 20 5 17 6 4 6 3 19 4 13 0 10 7 2 8 14 8 10 14 11 7 11 7 10 13 9 10 5 14 16 14 0 18 14 */ std::vector iVec; fc(iVec, 50, Irand{0, 20}); std::cout << "iVec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; int numberToDelete = 15; iVec.erase ( std::remove( iVec.begin(), iVec.end(), numberToDelete ), iVec.end() ); std::cout << "iVec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; } >>>> 'remove_if()' fonksiyonu : Belirli bir koşulu sağlayanları kaptan çıkartmaya yarıyor. * Örnek 1, //.. int main() { /* # OUTPUT # iVec : [50] => 16 17 13 17 5 4 5 18 8 3 2 8 16 8 9 13 14 8 0 19 8 18 15 19 6 6 14 9 15 8 12 18 9 12 0 2 8 15 17 1 16 6 20 18 15 12 0 4 15 11 iVec : [42] => 16 17 13 17 5 4 5 18 8 3 2 8 16 8 9 13 14 8 19 8 18 19 6 6 14 9 8 12 18 9 12 2 8 17 1 16 6 20 18 12 4 11 */ std::vector iVec; fc(iVec, 50, Irand{0, 20}); std::cout << "iVec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; int numberToDelete = 15; iVec.erase ( std::remove_if( iVec.begin(), iVec.end(), [numberToDelete](int a) {return a % numberToDelete == 0;} ), iVec.end() ); // '15' rakamına tam bölünenleri kaptan gerçekten de siliyoruz. std::cout << "iVec : [" << iVec.size() << "] => "; for(auto index : iVec) std::cout << index << " "; std::cout << "\n"; } * Örnek 2, //.. int main() { /* # OUTPUT # cumhur_yurdakul_emine_zerrin_yeliz_hasan_adnan_muslum_celal_yurdanur_akin_murathan_emirhan_agah_hilmi_ mert_emrecan_ugur_bora_muslum_kazim_ceyhun_utku_suleyman_mahir_ ----------------------------------------------------------------------------- cumhur_yurdakul_emine_zerrin_yeliz_hasan_muslum_celal_yurdanur_murathan_emirhan_hilmi_mert_emrecan_ugur_ bora_muslum_kazim_ceyhun_utku_suleyman_mahir_ ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 25, rname); pc(sVec, "_"); char characterToLookUp = 'a'; sVec.erase ( std::remove_if( sVec.begin(), sVec.end(), [characterToLookUp](const auto& name){ return !name.empty() && name.at(0) == characterToLookUp; } ), sVec.end() ); pc(sVec, "_"); } >>>> 'unique()' fonksiyonu : Ardışık ve eş değer öğelerin sayısını bire indirmektedir. 'remove()' gibi 'logic_end' konumunu döndürmektedir. '.operator==()' fonksiyonunu varsayılan fonksiyon olarak kullanmaktadır fakat bir 'overload' versiyonu da bizden 'predicate' istemektedir. Böylelikle 'custom' bir karşılaştırma kriteri oluşturabiliriz. * Örnek 1, //.. int main() { /* # OUTPUT # 3 | 3 | 2 | 1 | 0 | 1 | 3 | 0 | 1 | 1 | 1 | 1 | 3 | 0 | 2 | 1 | 3 | 2 | 3 | 2 | 1 | 2 | 1 | 0 | 2 | ----------------------------------------------------------------------------- 3 | 2 | 1 | 0 | 1 | 3 | 0 | 1 | 3 | 0 | 2 | 1 | 3 | 2 | 3 | 2 | 1 | 2 | 1 | 0 | 2 | ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 25, Irand{0, 3}); pc(iVec, " | "); iVec.erase ( std::unique(iVec.begin(), iVec.end()), iVec.end() ); pc(iVec, " | "); } * Örnek 2, //.. int main() { /* # OUTPUT # 39 | 80 | 5 | 63 | 2 | 66 | 63 | 87 | 36 | 12 | 38 | 26 | 41 | 37 | 69 | 69 | 13 | 26 | 2 | 62 | 74 | 46 | 85 | 28 | 50 | 36 | 19 | 66 | 37 | 16 | 92 | 23 | 52 | 78 | 71 | 40 | 37 | 60 | 42 | 63 | 24 | 64 | 100 | 85 | 64 | 43 | 49 | 46 | 34 | 27 | ----------------------------------------------------------------------------- 2 | 5 | 12 | 13 | 16 | 19 | 23 | 24 | 26 | 27 | 28 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | 52 | 60 | 62 | 63 | 64 | 66 | 69 | 71 | 74 | 78 | 80 | 85 | 87 | 92 | 100 | ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 50, Irand{0, 100}); pc(iVec, " | "); std::sort(iVec.begin(), iVec.end()); iVec.erase ( std::unique(iVec.begin(), iVec.end()), iVec.end() ); pc(iVec, " | "); } * Örnek 3, //.. int main() { /* # OUTPUT # 89 | 28 | 41 | 42 | 7 | 0 | 43 | 55 | 18 | 81 | 89 | 4 | 74 | 96 | 100 | 89 | 25 | 22 | 1 | 33 | 50 | 70 | 48 | 88 | 93 | 10 | 2 | 33 | 59 | 27 | 79 | 85 | 51 | 19 | 54 |91 | 94 | 7 | 36 | 44 | 77 | 29 | 11 | 78 | 61 | 83 | 78 | 50 | 36 | 34 | ----------------------------------------------------------------------------- 89 | 28 | 41 | 42 | 7 | 0 | 43 | 18 | 81 | 4 | 89 | 22 | 1 | 50 | 93 | 10 | 33 | 54 | 91 | 94 | 7 | 36 | 77 | 78 | 61 | 78 | ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 50, Irand{0, 100}); pc(iVec, " | "); iVec.erase ( std::unique(iVec.begin(), iVec.end(), [](int a, int b){ return a % 2 == b % 2; }), iVec.end() ); pc(iVec, " | "); } * Örnek 4, //.. int main() { /* # OUTPUT # kunter_esra_fahri_sadullah_tijen_suleyman_atakan_askin_naciye_nalan_eda_teslime_ ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 250, rname); sVec.erase ( std::unique( sVec.begin(), sVec.end(), [](const auto& s1, const auto& s2){ return s1.size() == s2.size(); } ), sVec.end() ); pc(sVec, "_"); } Şimdi de 'STL' içerisinde sonu '_copy' ile biten algoritmalara değinelim. Bu algoritmalar bir işlem yapılmış gibi bir 'range' i başka bir yere kopyalayan algoritmalardır. Örneğin, 'reverse' algoritması argüman aldığı 'range' içerisindeki öğeleri tersine çevirmektedir. 'reverse_copy' ise tersine çevirip bir başka 'range' e kopyalamaktadır. Benzer şekilde 'replace', belirli değerdeki öğeyi başka bir değer ile değiştiriyor. 'replace_copy' ise bu işlem yapılmış gibi başka bir yere kopyalamaktadır. Yine aynı şekilde 'remove' algoritması bir 'range' üzerinde lojik silme işlemi yapmakta. Dolayısla 'remove_copy' ise bu işlem yapılmış gibi başka bir yere kopyalamaktadır. Aşağıda bu konuya ilişkin örnekler verilmiştir: * Örnek 1, //.. int main() { /* # OUTPUT # sVec : [25] => sabriye celal necmiye fugen yilmaz celal kaan ahmet murathan sevilay zekai kazim hulusi malik bennur derin demet melike bekir cezmi efe yusuf malik sarp edip sList : [0] => sVec : [25] => sabriye celal necmiye fugen yilmaz celal kaan ahmet murathan sevilay zekai kazim hulusi malik bennur derin demet melike bekir cezmi efe yusuf malik sarp edip sList : [24] => sabriye celal necmiye fugen yilmaz celal kaan murathan sevilay zekai kazim hulusi malik bennur derin demet melike bekir cezmi efe yusuf malik sarp edip */ std::vector sVec; fc(sVec, 25, rname); std::cout << "sVec : [" << sVec.size() << "] => "; for(auto index : sVec) std::cout << index << " "; std::cout << "\n"; std::list sList; std::cout << "sList : [" << sList.size() << "] => "; for(auto index : sList) std::cout << index << " "; std::cout << "\n"; std::string nameToDelete = "ahmet"; // Hedef kap boş olduğundan 'back_inserter' kullandık. std::remove_copy(sVec.begin(), sVec.end(), std::back_inserter(sList), nameToDelete); std::cout << "sVec : [" << sVec.size() << "] => "; for(auto index : sVec) std::cout << index << " "; std::cout << "\n"; std::cout << "sList : [" << sList.size() << "] => "; for(auto index : sList) std::cout << index << " "; std::cout << "\n"; } * Örnek 2, //.. int main() { /* # OUTPUT # 660_967_205_733_606_547_529_547_488_349_190_659_102_821_306_149_330_61_795_29_674_863_387_557_438_619_762_491_ */ std::vector iVec; fc(iVec, 100, Irand{0, 1000}); std::unique_copy( iVec.begin(), iVec.end(), std::ostream_iterator{std::cout, "_"}, [](int a, int b) { return isprime(a) == isprime(b); } ); } * Örnek 3, 'reverse_copy()' fonksiyonu: //.. // Temsili bir implementasyonu. template OutIter ReverseCopy(BidIter beg, BidIter end, OutIter destBeg) { while( beg != end ) *destgBeg++ = *--end; return destBeg; } Bu gruptaki algoritmalara değinecek olursak; >>>> Yine bu tip algoritmaların sonuna '_if' gelirse de belirli şartları sağlayanları ele alıyor. Şöyleki: >>>>> 'remove_copy_if' : Belirli bir şartı sağlayanları başka bir 'range' aralığına kopyalamaktadır. * Örnek 1, //.. int main() { /* # OUTPUT # Vector : [10] => 02 Subat 1963 Cumartesi,07 Eylul 2007 Cuma,16 Kasim 1984 Cuma, 06 Ocak 1969 Pazartesi,11 Subat 1967 Cumartesi,20 Temmuz 1955 Carsamba,27 Eylul 1992 Pazar, 02 Subat 1952 Cumartesi,05 Ocak 1954 Sali,11 Ocak 1980 Cuma, ----------------------------------------------------------------------------- List : [0] => ----------------------------------------------------------------------------- Vector : [10] => 02 Subat 1963 Cumartesi,07 Eylul 2007 Cuma,16 Kasim 1984 Cuma, 06 Ocak 1969 Pazartesi,11 Subat 1967 Cumartesi,20 Temmuz 1955 Carsamba,27 Eylul 1992 Pazar, 02 Subat 1952 Cumartesi,05 Ocak 1954 Sali,11 Ocak 1980 Cuma, ----------------------------------------------------------------------------- List : [7] => 07 Eylul 2007 Cuma,16 Kasim 1984 Cuma,06 Ocak 1969 Pazartesi, 20 Temmuz 1955 Carsamba,27 Eylul 1992 Pazar,05 Ocak 1954 Sali,11 Ocak 1980 Cuma, ----------------------------------------------------------------------------- */ std::vector dVec; fc(dVec, 10, Date::random); std::list dList; std::cout << "Vector : [" << dVec.size() << "] => "; pc(dVec, ","); std::cout << "List : [" << dList.size() << "] => "; pc(dList, ","); int monthToDelete = 2; remove_copy_if ( dVec.begin(), dVec.end(), std::back_inserter(dList), [monthToDelete](const Date& date){ return date.month() == monthToDelete; } ); std::cout << "Vector : [" << dVec.size() << "] => "; pc(dVec, ","); std::cout << "List : [" << dList.size() << "] => "; pc(dList, ","); } Öte yandan yukarıdaki fonksiyonlara geçilen üçüncü argüman bir fonksiyon olabilir, 'operator()()' fonksiyonunu 'overload'eden bir sınıf olabilir ve bir 'lambda-expression' olabilir. * Örnek 1, Function Object kullanılması. //.. class LengthPredicate{ public: LengthPredicate(size_t length) : m_length{length} {} bool operator()(const std::string& s)const { return s.length() == m_length; } private: size_t m_length; }; int main() { std::vector myVec; fc(myVec, 100000, rname); // İlgili 'myVec' isimli kap 100000 öğeye sahiptir. size_t lengthToCount = 7; // Uzunluğu 7 olanların sayılması için kullanılacak. std::cout << "Uzunluğu " << lengthToCount << " olan " << std::count_if( myVec.begin(), myVec.end(), LengthPredicate{lengthToCount} ) << " adet isim vardır.\n"; // 'LengthPredicate' sınıf türünden geçici bir nesne oluşturuldu. O nesne üzerinden // 'operator()()' fonksiyonuna çağrıda bulunuldu. } * Örnek 2, Lambda Expression kullanılması. 'Lambda Expressions' ile yukarıdaki örnekteki sınıf kodunu da derleyiciye yazdırabiliriz. //.. int main() { std::vector myVec; fc(myVec, 100000, rname); // İlgili 'myVec' isimli kap 100000 öğeye sahiptir. size_t lengthToCount = 7; // Uzunluğu 7 olanların sayılması için kullanılacak. std::cout << "Uzunluğu " << lengthToCount << " olan " << std::count_if( myVec.begin(), myVec.end(), [lengthToCount] (const std::string& s) { return s.length() == lengthToCount; } ) << " adet isim vardır.\n"; //'operator()()' fonksiyonunun parametresi ve bloğu. } Şimdi de bir 'range' üzerinde çalışan diğer algoritma fonksiyonlarına değinelim: >>>> 'for_each' : Bir 'range' içerisindeki öğeleri bir fonksiyona argüman olarak göndermektedir. * Örnek 1, Temsili implementasyonu: //.. template F ForEach(Iter beg, Iter end, F callable) { while( beg != end ) callable(*beg++); return callable; } int main() { /* # OUTPUT # 1 | -1 | 2 | -2 | 3 | -3 | 4 | -4 | 5 | -5 | ----------------------------------------------------------------------------- 11 | -11 | 22 | -22 | 33 | -33 | 44 | -44 | 55 | -55 | ----------------------------------------------------------------------------- 1 | -1 | 2 | -2 | 3 | -3 | 4 | -4 | 5 | -5 | */ std::vector iVec{ 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 }; pc(iVec, " | "); int multiplyBy = 11; ForEach(iVec.begin(), iVec.end(), [multiplyBy](int& a){ a *= multiplyBy; }); pc(iVec, " | "); int divideBy = multiplyBy; std::for_each(iVec.begin(), iVec.end(), [divideBy](int& a){ a /= divideBy; }); std::for_each(iVec.begin(), iVec.end(), [](int a){ std::cout << a << " | "; }); } * Örnek 2, //.. int main() { /* # OUTPUT # nisan | bilal | derya | tugay | mert | zubeyde | atalay | fazilet | gulsah | sami | ----------------------------------------------------------------------------- nisan_can | bilal_can | derya_can | tugay_can | mert_can | zubeyde_can | atalay_can | fazilet_can | gulsah_can | sami_can | ----------------------------------------------------------------------------- */ std::list sList; fc(sList, 10, rname); pc(sList, " | "); std::string wordToAppend = "_can"; for_each(sList.begin(), sList.end(), [wordToAppend](std::string& name){ name += wordToAppend; }); pc(sList, " | "); } >>>> 'transform()' fonksiyonu : İki adet 'overload' versiyonu vardır. Bunlardan ilki 'range' içerisindeki öğeleri bir fonksiyona argüman olarak gönderiyor, o fonksiyondan elde edilen geri dönüş değerini bir 'range' içerisine yazmaktadır. İkinci 'overload' versiyonunda ise argüman olarak alınan fonksiyon iki parametreli. Bir 'range' den ilk argümanı, bir diğer 'range' den ikinci argümanı alacak ve iş bu fonksiyona gönderecek. Geri dönüş değerini de bir 'range' içerisine yazacaktır. * Örnek 1, Birinci 'overload' //.. // Temsili implementasyonu: template OutIter MyTransform(InIter beg, InIter end, OutIter destBeg, Func callable) { while( beg != end ) *destBeg++ = callable(*beg++); return destBeg; } int main() { /* # OUTPUT # 12 34 56 7 9 2 1 3 ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- 12 34 56 7 9 2 1 3 ----------------------------------------------------------------------------- 17 39 61 12 14 7 6 8 ----------------------------------------------------------------------------- 12 34 56 7 9 2 1 3 ----------------------------------------------------------------------------- 12 34 56 7 9 2 1 3 ----------------------------------------------------------------------------- */ std::vector iVec{ 12, 34, 56, 7, 9, 2, 1, 3 }; std::list iList; pc(iVec); pc(iList); MyTransform(iVec.begin(), iVec.end(), std::back_inserter(iList), [](int x){ return x + 5; }); pc(iVec); pc(iList); std::transform(iList.begin(), iList.end(), iList.begin(), [](int x){ return x - 5; }); pc(iVec); pc(iList); } * Örnek 2, İkinci 'overload' //.. // Temsili implementasyonu: template OutIter MyTransform(InIterOne beg, InIterOne end, InIterTwo begTwo, OutIter destBeg, Func callable) { while( beg != end ) *destBeg++ = callable(*beg++, *begTwo++); return destBeg; } int main() { /* # OUTPUT # 1 | 2 | 3 | 4 | 5 | 6 | ----------------------------------------------------------------------------- -1 | -2 | -3 | -4 | -5 | -6 | ----------------------------------------------------------------------------- 0 | 0 | 0 | 0 | 0 | 0 | ----------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ----------------------------------------------------------------------------- -1 | -2 | -3 | -4 | -5 | -6 | ----------------------------------------------------------------------------- 2 | 4 | 6 | 8 | 10 | 12 | ----------------------------------------------------------------------------- */ std::vector sourceOne{ 1, 2, 3, 4, 5, 6 }; std::vector sourceTwo{ -1, -2, -3, -4, -5, -6 }; std::vector destOne(sourceOne.size()); std::transform( sourceOne.begin(), sourceOne.end(), sourceTwo.begin(), destOne.begin(), [](int a, int b){ return a + b; } ); pc(sourceOne, " | "); pc(sourceTwo, " | "); pc(destOne, " | "); MyTransform( sourceOne.begin(), sourceOne.end(), sourceTwo.begin(), destOne.begin(), [](int a, int b){ return a - b; } ); pc(sourceOne, " | "); pc(sourceTwo, " | "); pc(destOne, " | "); } >>>> 'ostream_iterator' sınıfı: Aşağıdaki örneği inceleyelim. * Örnek 1, Aşağıdaki 'MyCopy' fonksiyonunu çıkış akımına yazma işlemi yapacak şekilde çalıştırabilir miyiz? //.. template OutIter MyCopy(InIter beg, InIter end, OutIter destBeg) { while( beg != end ) *destBeg++ = *beg++; return destBeg; } template class OstreamIterator{ public: OstreamIterator(std::ostream& other_os, const char* p = ", ") : m_p{p}, m_os{other_os} {} OstreamIterator& operator*() { return *this; } OstreamIterator& operator++() { return *this; } OstreamIterator& operator++(int) { return *this; } OstreamIterator& operator=(const T& value) { m_os << value << m_p; return *this; } private: const char* m_p; std::ostream& m_os; }; int main() { /* # OUTPUT # 383 886 777 915 793 335 386 492 649 421 362 27 690 59 763 926 540 426 172 736 211 368 567 429 782 ----------------------------------------------------------------------------- 383, 886, 777, 915, 793, 335, 386, 492, 649, 421, 362, 27, 690, 59, 763, 926, 540, 426, 172, 736, 211, 368, 567, 429, 782, ----------------------------------------------------------------------------- 383 | 886 | 777 | 915 | 793 | 335 | 386 | 492 | 649 | 421 | 362 | 27 | 690 | 59 | 763 | 926 | 540 | 426 | 172 | 736 | 211 | 368 | 567 | 429 | 782 | ----------------------------------------------------------------------------- nurdan - ziya - fahri - tufan - cemal - feraye - lale - halime - nuriye - enes - derin - yurdanur - zahide - kezban - cihat - kayhan - tugay - saniye - zubeyde - fikret - abdullah - erdem - fahri - aycan - melih - ----------------------------------------------------------------------------- 8.61022e+08 ` 2.78723e+08 ` 2.33665e+08 ` 2.14517e+09 ` 4.68703e+08 ` 1.10151e+09 ` 1.80198e+09 ` 1.31563e+09 ` 6.35723e+08 ` 1.36913e+09 ` 1.1259e+09 ` 1.05996e+09 ` 2.08902e+09 ` 6.28175e+08 ` 1.65648e+09 ` 1.13118e+09 ` 1.65338e+09 ` 8.59484e+08 ` 1.91454e+09 ` 6.08414e+08 ` 7.56899e+08 ` 1.73458e+09 ` 1.97359e+09 ` 1.49798e+08 ` 2.03866e+09 ` */ std::vector iVec; fc(iVec, 25, []{ return rand() % 1000; }); pc(iVec); // Çıkış akımının kendisini kullanırsak, // Yukarıdaki 'MyCopy' fonksiyon şablonunu kullanarak, MyCopy(iVec.begin(), iVec.end(), OstreamIterator{std::cout}); std::cout << "\n-----------------------------------------------------------------------------\n"; // Yukarıdaki 'MyCopy' fonksiyon şablonunu kullanarak, MyCopy(iVec.begin(), iVec.end(), OstreamIterator{std::cout, " | "}); std::cout << "\n-----------------------------------------------------------------------------\n"; std::list sList; fc(sList, 25, rname); MyCopy(sList.begin(), sList.end(), OstreamIterator{std::cout, " - "}); std::cout << "\n-----------------------------------------------------------------------------\n"; std::ofstream ofs{"ofs.txt"}; if(!ofs) { std::cerr << "out.txt dosyası oluşturulamadı.\n"; exit(EXIT_FAILURE); } std::deque dDeque; fc(dDeque, 25, rand); MyCopy(dDeque.begin(), dDeque.end(), OstreamIterator{ofs, " ` "}); } * Örnek 2, 'STL' içerisinde bulunan 'ostream_iterator' sınıfının kullanılması: 'iterator' başlık dosyasında bildirimi/tanımı yapılmıştır. //.. int main() { /* # OUTPUT # 383 -> 886 -> 777 -> 915 -> 793 -> 335 -> 386 -> 492 -> 649 -> 421 -> 362 -> 27 -> 690 -> 59 -> 763 -> 926 -> 540 -> 426 -> 172 -> 736 -> 211 -> 368 -> 567 -> 429 -> 782 -> */ std::vector iVec; fc(iVec, 25, []{ return rand() % 1000; }); std::copy(iVec.begin(), iVec.end(), std::ostream_iterator{std::cout, " -> "}); } * Örnek 3, Bir kapta bulunan isimlerden 'c' karakterinden 'n' taneye sahip olanları yazdırma işlemi: //.. int main() { /* # OUTPUT # [murat karaelmas] , [zeliha kabasakal] , [semsit uzunadam] , [hakki belgeli] , [aytac canbay] , [sevim temizkalp] , [yasar samanci] , [olcay komurcu] , [fikret samanci] , [fazilet kelepce] , ----------------------------------------------------------------------------- [hakki belgeli]_[sevim temizkalp]_[fikret samanci]_ */ std::vector sVec; fc(sVec, 10, []{ return "[" + rname() + ' ' + rfname() + "]"; }); std::ofstream ofs{"ofs.txt"}; if(!ofs) { std::cerr << "out.txt dosyası oluşturulamadı.\n"; exit(EXIT_FAILURE); } copy(sVec.begin(), sVec.end(), std::ostream_iterator{ofs, " , "}); ofs << "\n-----------------------------------------------------------------------------\n"; char c = 'i'; // İş bu karakterden int n = 2; // adet olanları arayacağız. auto f = [c, n](const std::string& name){ return std::count(name.begin(), name.end(), c) == n; }; std::copy_if(sVec.begin(), sVec.end(), std::ostream_iterator{ofs, "_"}, f); } >>>> 'swap_ranger()' fonksiyonu : İki 'range' içerisindeki öğeleri 'swap' eder. * Örnek 1, //.. int main() { std::vector sVec; fc(sVec, 10, rname); std::vector sList; fc(sList, 10, rtown); std::swap_ranges(sVec.begin(), sVec.end(), sList.begin()); // Hedef 'range' içerisinde yeterli öğe bulunmasından biz sorumluyuz. Büyüklüğü ise ilk 'range' // içerisindeki öğe sayısı belirlemektedir. Geri dönüş değeri ise hedef 'range' içerisine yazılan // son öğeden sonraki konum. BURADA 'container' ALMADIĞI İÇİN ÖĞELER FİİLEN TAKAS EDİLMEKTE. // DOLAYISIYLA MALİYETLİ BİR İŞLEMDİR. } >>>> Şimdi de sıralama yapan fonksiyonlara değinelim. Bu fonksiyonlar, 'sort()', 'partial_sort()', 'stable_sort()', 'nth_element()', 'partition()', 'stable_partition()' vb. fonksiyonlardır. Diğer yandan çoğu kap bünyesinde karşılaştırma operatörleri barındırır ki bu operatörler 'lexicographical_compare' şeklinde karşılaştırma yapmaktadır. Kapların birbirine eşit olabilmeleri için hem öğe sayıları hem de karşılıklı bütün öğeleri birbirine eşit olacak. Eşitlik olmadığında kaplardaki öğeler karşılıklı olarak birbirleri ile karşılaştırılırlar. İki öğenin birbirinden farklı olduğu ilk noktada, hangi öğe büyükse onun bulunduğu kap büyüktür kabul edilir. Son olarak bütün öğeler eşitken bir kaptaki öğe sayısı bittiyse, öğe sayısı bitmeyen kap daha büyüktür. * Örnek 1, //.. int main() { // Eşit olma durumu std::vector a{ 1, 2, 3, 4 }; std::vector b{ 1, 2, 3, 4 }; std::cout << ( a == b ) << "\n"; // OUTPUT => 1 // Büyük olma durumu std::vector c(1'000'000); // Bir milyon elemanı var ama bütün elemanları '0'. std::vector d{4}; // Bir elemanı var ama '4'. std::cout << ( d > c ) << "\n"; // OUTPUT => 1 // Büyük olma durumu v2 std::vector e{ 1, 2, 3, 4, 5}; std::vector f{ 1, 2, 3, 4 }; std::cout << ( e > f ) << "\n"; // OUTPUT => 1 } >>>> 'all_of()', 'any_of()', 'none_of()' fonksiyonları bir 'range' içerisindeki öğeleri bir kriteri karşılayıp karşılamadığını sınamaktadır. * Örnek 1, //.. int main() { /* # OUTPUT # */ int a[]{ 2, 6, 8, 10, 20, 60, 70, 90, 23, 50 }; // Bütün elemanlar kriteri karşılıyor mu? std::cout << std::boolalpha << ( std::all_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => false // Bütün elemanlar ktiteri karşılamıyor mu? std::cout << std::boolalpha << ( std::none_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => false // Elemanlardan kriteri karşılayan var mı? std::cout << std::boolalpha << ( std::any_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => true return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # */ std::vector a; // Bütün elemanlar kriteri karşılıyor mu? : Olmayan öğe için çift desek yanlış bir önerme olmaz. std::cout << std::boolalpha << ( std::all_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => true // Bütün elemanlar ktiteri karşılamıyor mu? : Olmayan öğe için çift desek yanlış bir önerme olmaz. std::cout << std::boolalpha << ( std::none_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => true // Elemanlardan kriteri karşılayan var mı? : Hiç eleman yok ki hangisi çift olsun. std::cout << std::boolalpha << ( std::any_of(std::begin(a), std::end(a), [](int a){ return a % 2 == 0; }) ) << "\n"; // OUTPUT => false return 0; } Şimdi de yukarıda isimleri zikredilen fonksiyonları inceleyelim: >>>>> 'sort()' : 'random_access_iterator' istemektedir. Varsayılan fonksiyon objesi ise 'std::less'. Dolayısla küçükten büyüğe doğru sıralama yapacaktır. Sınıf türünden nesneleri sıralayacaksak, '.operator<()' fonksiyonu 'overload' edilmiş olması gerekiyor. Velevki büyükten küçüğe doğru sıralamak isteseydik ya 'reverse_iterator' kullanacaktık ya da 'std::greater' fonksiyon objesini. Ek olarak kendi kriterimize göre de sıralama yapabiliriz. * Örnek 1, //.. int main() { std::vector sVec; fc(sVec, 100'000, []{ return rname() + ' ' + rfname(); }); std::sort(sVec.begin(), sVec.end()); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } pc(sVec, "\n", ofs); } * Örnek 2, //.. int main() { std::vector sVec; fc(sVec, 100'000, []{ return rname() + ' ' + rfname(); }); std::sort( sVec.begin(), sVec.end(), [](const std::string& s1, const std::string& s2){ return s1.length() < s2.length() || ( s1.length() == s2.length() && s1 < s2 ) } ); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } pc(sVec, "\n", ofs); } >>>>> 'stable_sort()' : Aynı anahtara sahip değerlerin izafi konumları sıralamadan sonra da korunma garantisini veren sıralamadır. '.sort()' bu GARANTİYİ VERMEMEKTEDİR (Bkz. "https://en.cppreference.com/w/cpp/algorithm/stable_sort") Yani sıralama öncesinde aynı 'key' değerine sahip 'value' lerin kendi içindeki sıralaması, sıralamadan sonra da aynı kalmaktadır. * Örnek 1, //.. int main() { std::vector> myVec; fc( myVec, 100, []{ return std::make_pair( Irand{ 0, 100 }(), rname() ); } ); /* 0 hande 3 cetin 3 gulden 4 burhan 4 pinat 8 nalan 9 engin 11 celal 13 yurdakul 14 hakki 14 kayhan 14 kelami 18 refik 19 candan 21 busra 21 kaan 21 sidre 22 nurdan 23 nahit 24 busra 24 ismail 25 polathan 26 aydan 27 gulsah 28 mukerrem 30 metin 34 aslican 35 nasrullah 36 nisan 36 seyhan 37 arda 37 tarkan 38 keriman 40 mukerrem 41 bilgin 42 bennur 43 caner 43 temel 43 yusuf 44 cumhur 44 devlet 45 hasan 46 yavuz 48 melisa 48 papatya 51 halime 52 kurthan 56 beste 57 gulsen 58 gurbuz 58 sezen 58 tarcan 63 cezmi 63 ciler 64 haluk 65 aykut 65 cemal 67 gurbuz 69 burhan 69 derya 70 lamia 71 atil 71 esra 71 tevfik 73 ceyhan 75 naci 75 samet 76 cahit 77 belgin 78 muruvvet 78 selin 79 tayyip 80 diana 80 hilal 80 okan 81 cengiz 81 kaan 82 ercument 82 kaan 82 tansu 83 edip 84 recep 85 cahit 85 hasan 85 zekai 87 beste 87 zekai 88 atil 90 pakize 91 ercument 92 atif 95 askin 95 beste 96 garo 96 nazif 98 necmettin 98 sadiye 100 burhan 100 sidre 100 tufan */ // İlgili kaptaki öğeler küçükten büyüğe doğru sıralandılar. Buradaki sıralama kriteri ise // 'first' değeri küçük olan küçük, 'first' değeri eşit ise 'second' değeri küçük olan küçüktür. std::sort(myVec.begin(), myVec.end()); auto myLambda = [](const auto& p1, const auto& p2){ return p1.second < p2.second; }; /* 58 abdi 66 abdulmuttalip 98 afacan 67 alev 55 aslican 67 aslihan 19 atil 90 aydan 59 aydan 86 ayse 99 aytac 50 aziz 75 azmi 64 baran 32 beril 53 beste 3 binnur 52 birhan 81 can 44 candan 13 candan 40 cansu 34 celal 55 cemile 28 cengiz 38 cihat 83 ciler 65 cumhur 54 cuneyt 28 dilber 55 dogan 60 ece 26 egemen 89 enes 16 engin 18 ercument 97 esen 78 esen 95 esin 59 esra 67 fadime 20 fadime 36 fahri 30 feramuz 89 galip 23 garo 55 gazi 47 gursel 97 hakan 71 hakan 85 hakki 82 hande 44 hilal 32 hilal 89 hulusi 2 ismail 82 kamil 25 kelami 33 korhan 71 mehmet 38 melih 35 melike 40 metin 68 muzaffer 55 nazife 24 nihal 24 nurdan 28 nurullah 78 nusret 84 perihan 84 petek 16 polat 37 polathan 7 sade 37 sadi 38 sadiye 62 sadullah 97 samet 11 sami 98 sefa 56 sevilay 99 sezer 20 sidre 46 soner 18 suleyman 98 taci 69 tarkan 86 tarkan 61 tarkan 59 teoman 58 teoman 55 tugra 98 tugra 5 ufuk 95 umit 85 utku 65 yasar 63 yavuz 32 zahide 33 zubeyde */ std::sort(myVec.begin(), myVec.end(), myLambda); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } ofs << std::left; // Sola yaslandı yazılar. for(const auto& [ID, name] : myVec) // Structural Binding kullanıldı { ofs << std::setw(3) << ID << " " << name << "\n"; // Vektördeki öğeler bir dosyaya yazıldı. } } * Örnek 2, //.. int main() { std::vector> myVec; fc(myVec, 100, []{ return std::make_pair( Irand{ 0, 100 }(), rname() ); }); /* 2 nalan 2 soner 6 sadettin 7 pakize 8 birhan 10 adnan 10 ece 11 dilek 12 utku 13 gulden 13 irmak 16 sevda 16 tuncer 17 nuri 19 durriye 19 taner 22 cengiz 23 nusret 24 rumeysa 25 hasan 27 adem 27 cahide 28 caner 28 celik 28 soner 29 azmi 29 ferhunde 31 aylin 32 deniz 32 helin 32 pinat 34 yurdagul 36 aycan 36 azmi 37 ayla 37 sefer 38 malik 39 hikmet 39 selenay 40 halime 40 julide 42 tansu 45 galip 45 tevfik 48 haldun 49 zahit 50 tarik 51 soner 53 kaan 53 nevsin 53 sami 54 birhan 54 garo 54 nazli 54 sami 56 olcay 57 selenay 58 devlet 60 canan 61 efecan 62 naz 63 sidre 65 korhan 66 sevilay 67 zeliha 68 cetin 69 bekir 69 kamile 69 nagehan 70 hakki 71 gursel 72 kerem 75 handesu 76 durmus 76 murat 77 huseyin 78 izzet 78 malik 78 suheyla 79 alican 79 emrecan 79 yurdakul 80 abdullah 80 sevilay 83 melike 84 lamia 85 halime 88 cemal 88 korhan 88 pinat 91 poyraz 93 yusuf 94 busra 94 tekin 94 umit 95 korhan 96 ceylan 99 fugen 99 nefes 100 naciye */ // İlgili kaptaki öğeler küçükten büyüğe doğru sıralandılar. Buradaki sıralama kriteri ise // 'first' değeri küçük olan küçük, 'first' değeri eşit ise 'second' değeri küçük olan küçüktür. std::sort(myVec.begin(), myVec.end()); auto myLambda = [](const auto& p1, const auto& p2){ return p1.second < p2.second; }; /* 63 abdullah 53 adnan 43 atalay 19 atil 23 atil 6 aykut 75 ayla 31 aylin 35 aziz 0 baran 45 bekir 44 belgin 78 beril 78 berk 23 bilal 86 bilgin 82 bulent 2 canan 51 canan 13 candan 24 cebrail 44 celik 29 ciler 43 cumhur 63 deniz 20 derin 23 derya 52 diana 31 emine 32 emine 94 emine 64 fadime 47 ferhat 30 fuat 99 fuat 11 gulsen 94 gulsen 37 gursel 93 haluk 39 handan 30 helin 11 hikmet 14 hilmi 72 hulki 1 hulya 87 hulya 1 iffet 35 irmak 31 izzet 32 izzet 34 izzet 71 izzet 49 kasim 89 kayahan 17 kerem 68 menekse 74 metin 40 murat 39 mustafa 33 muzaffer 66 muzaffer 41 naci 47 nazife 79 necmettin 69 nefes 0 nihal 87 niyazi 74 nusret 61 okan 23 onat 7 pakize 11 papatya 69 pelinsu 97 pelinsu 60 polathan 79 recep 83 recep 22 sefer 50 sefer 80 selin 27 su 83 su 99 suheyla 96 tarkan 80 tayfun 34 teoman 57 teoman 56 tijen 57 tijen 53 tuncer 78 tuncer 89 tuncer 91 turhan 35 yalcin 23 yasin 33 yelda 82 yesim 9 zahide 22 zarife 61 ziya */ std::stable_sort(myVec.begin(), myVec.end(), myLambda); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } ofs << std::left; // Sola yaslandı yazılar. for(const auto& [ID, name] : myVec) // Structural Binding kullanıldı { ofs << std::setw(3) << ID << " " << name << "\n"; // Vektördeki öğeler bir dosyaya yazıldı. } } * Örnek 3, //.. #include #include #include #include struct Employee { int age; std::string name; // Does not participate in comparisons }; bool operator<(const Employee & lhs, const Employee & rhs) { return lhs.age < rhs.age; } int main() { /* # OUTPUT # 32, Arthur 108, Zaphod 108, Ford */ std::vector v = { {108, "Zaphod"}, {32, "Arthur"}, {108, "Ford"}, }; std::stable_sort(v.begin(), v.end()); for (const Employee & e : v) std::cout << e.age << ", " << e.name << '\n'; } >>>>> 'partial_sort()' : İlk 'n' tane öğeyi sıralı hale getiriyor. Örneğin, 50'000 tane öğrenci içerisinden en iyi 10 tanesini seçeceksek bu algoritma karlı olacaktır. En iyi on adet öğrenci bulmak için 50'000 tane öğeyi sıralamaya gerek yoktur. Birinci ve üçüncü parametre üzerinde gezilecek 'range' için başlangıç ve bitiş konumlarıyken, ikinci parametre ise sıralanmış 'range' in 'n' adedi. * Örnek 1, //.. int main() { /* # OUTPUT # ahmet sivri ali dokmeci gunay daglarca haluk cilingir handan engerek tayyar adiguzel taner yersiz sezen semiz soner serce sadi haselici mukerrem elkizi seyhan kosnuk poyraz yolyapan nuriye edepli necati sessiz yesim tamirci korhan cangoz julide korukcu melisa kelleci iffet canlikaya ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 20, []{ return rname() + ' ' + rfname(); }); int n = 5; // İlk beş sıralı olacaktır. std::partial_sort(sVec.begin(), sVec.begin() + n, sVec.end()); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } print(sVec, "\n", ofs); } >>>>> 'nth_element()' : Kaptaki bütün öğeler sıralanmış olması durumunda o öğe hangi konumda olacaksa, sıralama yapılmaksızın ilgili öğe yine aynı konumda olacaktır. * Örnek 1, //.. int main() { /* # OUTPUT # akin sarikafa asim uslu aycan yagizeli aynur degirmenci beyhan boztas beyhan edepli bora reis bora unkapani cemile malkaciran ceyhun gedik devrim unalmis ercument koralp kezban agaoglu necmi karakuzu nusret resimci selin sener sinem karasaban tonguc uluocak yurdagul gilgamis zerrin temizel ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 20, []{ return rname() + ' ' + rfname(); }); int n = 5; // Beşinci konumdaki öğe. std::nth_element(sVec.begin(), sVec.begin() + n, sVec.end()); // OUTPUT => 5th element : beyhan edepli std::cout << n << "th element : " << *(sVec.begin() + n) << "\n"; std::sort(sVec.begin(), sVec.end()); std::ofstream ofs{ "out.txt" }; if(!ofs) { std::cerr << "out.txt dosyasi olusturulamadi.\n"; exit(EXIT_FAILURE); } print(sVec, "\n", ofs); } * Örnek 2, Medyan Hesaplaması (Mülakat Sorusu) : Sıralı olması durumunda ortadaki değer. //.. template auto get_median(Iter beg, Iter end, UnPred f) { auto midPoint = std::distance(beg, end) / 2; std::nth_element(beg, midPoint, end, f); return *std::next(beg, midPoint); } int main() { //... } >>>>> 'partition()' : En önemli algoritmalardan birisidir. Bir kritere göre partisyon yapmakta. Kriteri sağlayanlar başta, sağlamayanlar sonda. 'Partition Point' ise koşulu sağlamayanlardan ilkinin konumu demektir. * Örnek 1, //.. int main() { /* # OUTPUT # erdem ugursuz | kerim komurcu | sevilay takes | kamile elebasi | selin yurekli | cetin azmak | berk kaplan | seyhan kotek | suphi karaorman | can koralp | ----------------------------------------------------------------------------- Object until the Partition Point => 8, suphi karaorman From the begining until the point => erdem ugursuz < kerim komurcu < sevilay takes < kamile elebasi < selin yurekli < cetin azmak < berk kaplan < seyhan kotek < From the point until the end => suphi karaorman | can koralp | */ std::vector sVec; fc(sVec, 10, []{ return rname() + ' ' + rfname(); }); char c = 'e'; // Bu karaktere sahip olanları yazının başına alacağız. if( auto partitionPoint = std::partition( sVec.begin(), sVec.end(), [c](const std::string& s1){ return s1.find(c) != std::string::npos; } ); partitionPoint != sVec.end() ) { print(sVec, " | "); std::cout << "Object until the Partition Point => " << std::distance(sVec.begin(), partitionPoint) << ", " << *partitionPoint << "\n"; std::cout << "From the begining until the point => "; std::copy( sVec.begin(), partitionPoint, std::ostream_iterator{std::cout, " < "} ); std::cout << "\n"; std::cout << "From the point until the end => "; std::copy(partitionPoint, sVec.end(), std::ostream_iterator{std::cout, " | "}); } } >>>>> 'stable_partition()' : İzafi konumlarını koruyarak yukarıdaki sıralamayı yapmaktadır. * Örnek 1, //.. int main() { /* # OUTPUT # tarik oztoklu | cahit zengin | kaan eloglu | yasar simsek | melek onaran | celal yavasakan | turgut koylu | zekai baturalp | agah hamsikoylu | bennur sonuzun | ----------------------------------------------------------------------------- cahit zengin | kaan eloglu | yasar simsek | melek onaran | celal yavasakan | zekai baturalp | bennur sonuzun | tarik oztoklu | turgut koylu | agah hamsikoylu | ----------------------------------------------------------------------------- Object until the Partition Point => 7, tarik oztoklu From the begining until the point => cahit zengin < kaan eloglu < yasar simsek < melek onaran < celal yavasakan < zekai baturalp < bennur sonuzun < From the point until the end => tarik oztoklu | turgut koylu | agah hamsikoylu | */ std::vector sVec; fc(sVec, 10, []{ return rname() + ' ' + rfname(); }); print(sVec, " | "); char c = 'e'; // Bu karaktere sahip olanları yazının başına alacağız. auto partitionPoint = std::stable_partition( sVec.begin(), sVec.end(), [c](const std::string& s1){ return s1.find(c) != std::string::npos; } ); print(sVec, " | "); std::cout << "Object until the Partition Point => " << std::distance(sVec.begin(), partitionPoint) << ", " << *partitionPoint << "\n"; std::cout << "From the begining until the point => "; std::copy(sVec.begin(), partitionPoint, std::ostream_iterator{std::cout, " < "}); std::cout << "\n"; std::cout << "From the point until the end => "; std::copy(partitionPoint, sVec.end(), std::ostream_iterator{std::cout, " | "}); } >>>>> Sonu '_copy' ile biten sıralama algoritmaları, tıpkı sonu diğer sonu '_copy' ile bitenler gibi, sıralanmış halini başka bir 'range' e yazmaktadır. >>>>> 'is_sorted()' : Bir 'range' sıralı mı değil mi kontrol eder. 'bool' veri tipinde geri dönüş değerine sahiptir. >>>>> 'is_sorted_until()' : Kaçıncı indisli öğeye kadar sıralı olduğunu kontrol ediyor. Bir 'iterator' döndürmektedir, bu da sıralamayı bozan ilk öğenin konum bilgisidir. >>>>> 'is_partitioned()' : Benzer şekilde bir 'range' öğelerinin 'partition' şeklinde sıralanıp sıralanmadığını kontrol etmektedir. >>>>> 'heap' algoritmaları: 'make_heap', 'push_heap', 'pop_heap' ve 'sort_heap' fonksiyonlarından meydana gelir. Detaylarına daha sonra değinilecektir. Şimdi de diğer sıralama algoritmalarına değinelim. Bu algoritmaların, yukarıda isimleri zikredilenlerden farkı, üzerinde koştuğu 'range' öğelerinin belli şartlarda sıralanmış olması gerekmektedir. Dolayısıyla bu tip algoritma fonksiyonlarına 'Sorted Range Algorithm' da denir. Sıralanmamış 'range' üzerinde bu algoritmaları çalıştırmak ise 'Tanımsız Davranış' a neden olacaktır. Fakat öncesinde sıralı haldeki veri yapılarında kullanılan 'lower_bound', 'upper_bound' ve 'equal_range' kelimelerin anlamlarını anlatalım: >>>>> 'lower_bound' : Anahtar olan öğeden Büyük-Eşit olan ilk öğenin konumu. Bir diğer değişle ilgili anahtar değerini 'insert' edebileceğim İLK konum bilgisidir. * Örnek 1, Sıralı Veri Yapısı => 2 3 3 3 5 5 7 7 7 8 12 16 19 Key: 7, lower_bound => ^ Key: 4, lower_bound => ^ >>>>> 'upper_bound' : Anahtar olan öğeden Büyük olan ilk öğenin konumudur. Bir diğer değişle ilgili anahtar değerini 'insert' edebileceğim SON konum bilgisidir. * Örnek 1, Sıralı Veri Yapısı => 2 3 3 3 5 5 7 7 7 8 12 16 19 Key: 7, lower_bound => ^ , upper_bound => ^ Key: 4, lower_bound => ^ upper_bound => ^ Key: 13, lower_bound => ^ , upper_bound => ^ >>>>> 'equal_range' : Öyle bir 'range' düşününki 'lower_bound' ve 'upper_bound' konum bilgilerinden meydana gelsin. Bir 'pair of iterator' döndürmektedir. Bu 'pair' sınıfının 'first' isimli veri elemanı 'lower_bound' konumunu, 'second' isimli veri elemanı da 'upper_bound' konumunu tutmaktadır. * Örnek 1, Sıralı Veri Yapısı => 2 3 3 3 5 5 7 7 7 8 12 16 19 Key: 7, lower_bound => ^ , upper_bound => ^ , equal_range => ^ ^ Key: 13, lower_bound => ^ , upper_bound => ^ , equal_range => // 'upper_bound' ve 'lower_bound' konumları // aynı olduğundan 'range' BOŞTUR. Aşağıdaki örneği inceleyelim: * Örnek 1, //.. int main() { /* # OUTPUT # 0 1 2 3 6 7 8 9 11 12 13 16 18 19 20 22 23 25 27 29 ----------------------------------------------------------------------------- 6 rakami için lower_bound konumu : 4 6 rakami için upper_bound konumu : 5 [1] => 6 ----------------------------------------------------------------------------- */ std::multiset mySet; fcs(mySet, 20, []{ return rand() % 30; }); print(mySet); auto iter_lower = mySet.lower_bound(6); // '6' rakamı için 'lower_bound' konumu. std::cout << 6 << " rakami için lower_bound konumu : " << std::distance(mySet.begin(), iter_lower) << "\n"; auto iter_upper = mySet.upper_bound(6); // '6' rakamı için 'upper_bound' konumu. std::cout << 6 << " rakami için upper_bound konumu : " << std::distance(mySet.begin(), iter_upper) << "\n"; std::cout << "[" << std::distance(iter_lower, iter_upper) << "] => "; print(iter_lower, iter_upper); // auto p = mySet.equal_range(6); // print(p.first, p.second); return 0; } Şimdi de bu algoritmaları görelim: >>>>> '.binary_search()' : O n(log)n karmaşıklığında, sıralanmış bir 'range' üzerinde 'binary_search' işlemi gerçekleştirmektedir. Varsayılan sıralama kriteri 'std::less' fakat sıralama başka bir şekilde yapıldıysa o kriteri de yine bu fonksiyona argüman olarak geçmemiz gerekmektedir. Var mı yok mu sorgulaması yapar. * Örnek 1, //.. int main() { /* # OUTPUT # antalya artvin bilecik bilecik corum hatay kutahya manisa sivas yozgat ----------------------------------------------------------------------------- Aranacak sehir : yozgat Aranan [yozgat] isimli sehir bulundu... */ std::vector sVec; fcs(sVec, 10, rtown); std::sort(sVec.begin(), sVec.end()); print(sVec); std::string town; std::cout << "Aranacak sehir : "; std::cin >> town; if(std::binary_search(sVec.begin(), sVec.end(), town)) std::cout << "Aranan [" << town << "] isimli sehir bulundu...\n"; else std::cout << "Aranan [" << town << "] isimli sehir bulunamadı...\n"; } * Örnek 2, //.. auto f = [](const std::string& s1, const std::string& s2){ return s1.length() < s2.length() || ( s1.length() == s2.length() && s1 < s2 ) }; int main() { /* # OUTPUT # antalya artvin bilecik bilecik corum hatay kutahya manisa sivas yozgat ----------------------------------------------------------------------------- Aranacak sehir : yozgat Aranan [yozgat] isimli sehir bulundu... */ std::vector sVec; fcs(sVec, 10, rtown); std::sort(sVec.begin(), sVec.end(), f); print(sVec); std::string town; std::cout << "Aranacak sehir : "; std::cin >> town; if(std::binary_search(sVec.begin(), sVec.end(), town, f)) std::cout << "Aranan [" << town << "] isimli sehir bulundu...\n"; else std::cout << "Aranan [" << town << "] isimli sehir bulunamadı...\n"; } >>>>> 'lower_bound()', 'upper_bound()' ve 'equal_range()' fonksiyonları ki bunlar sırasıyla ilgili değerden büyük-eşit olan ilk değerin konumu, ilgili değerden büyük olan ilk değerin konumu ve bu iki konumun oluşturduğu 'range' şeklindedir. * Örnek 1, //.. int main() { /* # OUTPUT # bartin bolu bolu erzincan igdir kirikkale kirsehir kutahya kutahya van ----------------------------------------------------------------------------- Aranacak sehir : bolu [2] => bolu bolu ----------------------------------------------------------------------------- */ std::vector sVec; fcs(sVec, 10, rtown); std::sort(sVec.begin(), sVec.end()); print(sVec); std::string town; std::cout << "Aranacak sehir : "; std::cin >> town; std::cout << "[" << std::distance( std::lower_bound(sVec.begin(), sVec.end(), town), std::upper_bound(sVec.begin(), sVec.end(), town) ) << "] => "; auto bounds = std::equal_range(sVec.begin(), sVec.end(), town); print(bounds.first, bounds.second); } * Örnek 2, sırayı bozmadan bir kaba ekleme yapmak: //.. int main() { /* # OUTPUT # Eklenecek isim: murat => ayhan murat salim turgut ----------------------------------------------------------------------------- Eklenecek isim: kezban => ayhan kezban murat salim turgut ----------------------------------------------------------------------------- Eklenecek isim: melike => ayhan kezban melike murat salim turgut ----------------------------------------------------------------------------- Eklenecek isim: naz => ayhan kezban melike murat naz salim turgut ----------------------------------------------------------------------------- Eklenecek isim: tayfun => ayhan kezban melike murat naz salim tayfun turgut ----------------------------------------------------------------------------- */ std::vector sVec{ "ayhan", "salim", "turgut" }; for(int i = 0; i < 5; ++i) { std::string name = rname(); std::cout << "Eklenecek isim: " << name << " => "; sVec.insert( std::lower_bound( sVec.begin(), sVec.end(),name ), name ); print(sVec); } return 0; } * Örnek 3, sırayı bozmadan bir kaba ekleme yapmak: //.. auto f = [](const std::string& s1, const std::string& s2){ return s1.length() < s2.length() || (s1.length() == s2.length() && s1 < s2); }; int main() { /* # OUTPUT # Eklenecek isim: nalan => ayhan nalan salim turgut ----------------------------------------------------------------------------- Eklenecek isim: adem => adem ayhan nalan salim turgut ----------------------------------------------------------------------------- Eklenecek isim: ceyda => adem ayhan ceyda nalan salim turgut ----------------------------------------------------------------------------- Eklenecek isim: korhan => adem ayhan ceyda nalan salim korhan turgut ----------------------------------------------------------------------------- Eklenecek isim: muzaffer => adem ayhan ceyda nalan salim korhan turgut muzaffer ----------------------------------------------------------------------------- */ std::vector sVec{ "ayhan", "salim", "turgut" }; for(int i = 0; i < 5; ++i) { std::string name = rname(); std::cout << "Eklenecek isim: " << name << " => "; sVec.insert( std::lower_bound( sVec.begin(), sVec.end(),name, f ), name); print(sVec); } return 0; } >>>>> 'set_union()', 'set_intersection()', 'set_difference()' ve 'set_symmetric_difference()' fonksiyonları sırasıyla Birleşim, Kesişim, Fark ve Simetrik Fark anlamlarında kullanılırlar. Buradaki; >>>>>> Birleşim, iki kümenin birleşimi manasında. * Örnek 1, //.. int main() { /* # OUTPUT # berk cahide gizem gulden hakan jade kerim lamia murathan nuriye perihan perihan sevilay sinem tanju tugay tuncer tuncer yurdanur zeliha ----------------------------------------------------------------------------- aslican beril binnaz birhan eda edip ediz hulki kasim melisa muslum nagehan necati refika sumeyye taci teoman teslime zarife zekai ----------------------------------------------------------------------------- aslican beril berk binnaz birhan cahide eda edip ediz gizem gulden hakan hulki jade kasim kerim lamia melisa murathan muslum nagehan necati nuriye perihan perihan refika sevilay sinem sumeyye taci tanju teoman teslime tugay tuncer tuncer yurdanur zarife zekai zeliha */ std::vector sVecOne; fcs(sVecOne, 20, rname); std::sort(sVecOne.begin(), sVecOne.end()); print(sVecOne); std::vector sVecTwo; fcs(sVecTwo, 20, rname); std::sort(sVecTwo.begin(), sVecTwo.end()); print(sVecTwo); std::set_union( sVecOne.begin(), sVecOne.end(), sVecTwo.begin(), sVecTwo.end(), std::ostream_iterator{std::cout, " "} ); return 0; } >>>>>> Kesişim, iki kümenin kesişimi manasında. * Örnek 1, //.. int main() { /* # OUTPUT # anil asim baran fadime fazilet ferhat galip hulya kenan olcay orkun sevda su tansu tarkan tayfun yasar yasar zahide zeliha ----------------------------------------------------------------------------- asim ata beril berk celik esra fahri furkan haluk hilmi kurthan melisa mustafa nagehan nihat sadri sinem temel yalcin zarife ----------------------------------------------------------------------------- asim */ std::vector sVecOne; fcs(sVecOne, 20, rname); std::sort(sVecOne.begin(), sVecOne.end()); print(sVecOne); std::vector sVecTwo; fcs(sVecTwo, 20, rname); std::sort(sVecTwo.begin(), sVecTwo.end()); print(sVecTwo); std::set_intersection( sVecOne.begin(), sVecOne.end(), sVecTwo.begin(), sVecTwo.end(), std::ostream_iterator{std::cout, " "} ); return 0; } >>>>>> Fark, iki kümenin farkı manasında. * Örnek 1, //.. int main() { /* # OUTPUT # afacan dost durmus durriye gul gunay necmi necmi olcay saadet sadiye sadri sevda seyhan su sumeyye taner taner yilmaz yusuf ----------------------------------------------------------------------------- abdulmuttalip anil burhan celik cemile diana ediz efe enes ferhat gulden hulusi kayahan kazim nazife sevda sezen tansu yasemin yurdanur ----------------------------------------------------------------------------- afacan dost durmus durriye gul gunay necmi necmi olcay saadet sadiye sadri seyhan su sumeyye taner taner yilmaz yusuf */ std::vector sVecOne; fcs(sVecOne, 20, rname); std::sort(sVecOne.begin(), sVecOne.end()); print(sVecOne); std::vector sVecTwo; fcs(sVecTwo, 20, rname); std::sort(sVecTwo.begin(), sVecTwo.end()); print(sVecTwo); std::set_difference( sVecOne.begin(), sVecOne.end(), sVecTwo.begin(), sVecTwo.end(), std::ostream_iterator{std::cout, " "} ); return 0; } >>>>>> Simetrif Fark, iki kümenin birleşiminden kesişiminin çıkartılması manasında kullanılır. * Örnek 1, //.. int main() { /* # OUTPUT # aslican binnur burak burhan ceylan cezmi dilber dost ece kamil kaya kenan nagehan necati refika sadiye selenay sidre ugur yurdakul ----------------------------------------------------------------------------- atil berivan cemal cemre cengiz erdem esen esra feramuz jade kamile kamile melek nahit nurdan tunc turgut yasemin yeliz zubeyde ----------------------------------------------------------------------------- aslican atil berivan binnur burak burhan cemal cemre cengiz ceylan cezmi dilber dost ece erdem esen esra feramuz jade kamil kamile kamile kaya kenan melek nagehan nahit necati nurdan refika sadiye selenay sidre tunc turgut ugur yasemin yeliz yurdakul zubeyde */ std::vector sVecOne; fcs(sVecOne, 20, rname); std::sort(sVecOne.begin(), sVecOne.end()); print(sVecOne); std::vector sVecTwo; fcs(sVecTwo, 20, rname); std::sort(sVecTwo.begin(), sVecTwo.end()); print(sVecTwo); std::set_symmetric_difference( sVecOne.begin(), sVecOne.end(), sVecTwo.begin(), sVecTwo.end(), std::ostream_iterator{std::cout, " "} ); return 0; } >>>>> 'next_permutation()' : Bir 'range' içerisindeki bütün kombinasyonları ele alan fonksiyondur. Test fonksiyonu olarak işe yarayabilir. * Örnek 1, //.. int main() { /* # OUTPUT # arda aylin cahit ----------------------------------------------------------------------------- sabriye tevfik yunus ----------------------------------------------------------------------------- arda aylin cahit ----------------------------------------------------------------------------- arda cahit aylin ----------------------------------------------------------------------------- aylin arda cahit ----------------------------------------------------------------------------- aylin cahit arda ----------------------------------------------------------------------------- cahit arda aylin ----------------------------------------------------------------------------- cahit aylin arda ----------------------------------------------------------------------------- Total number of combination : 6 */ std::vector sVecOne; fcs(sVecOne, 3, rname); std::sort(sVecOne.begin(), sVecOne.end()); print(sVecOne); int counter = 0; do { print(sVecOne); ++counter; } while(std::next_permutation(sVecOne.begin(), sVecOne.end())); std::cout << "Total number of combination : " << counter << "\n"; return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # Total number of combination : 24 */ std::vector sVecOne{ "A", "B", "C", "D" }; int counter = 0; do { ++counter; } while(std::next_permutation(sVecOne.begin(), sVecOne.end())); std::cout << "Total number of combination : " << counter << "\n"; return 0; } * Örnek 3, Bizim belirlediğimiz bir sıralama kriteri olsaydı: //.. int main() { /* # OUTPUT # canan sevda fazilet ----------------------------------------------------------------------------- gurbuz rumeysa sadettin ----------------------------------------------------------------------------- canan sevda fazilet ----------------------------------------------------------------------------- canan fazilet sevda ----------------------------------------------------------------------------- sevda canan fazilet ----------------------------------------------------------------------------- sevda fazilet canan ----------------------------------------------------------------------------- fazilet canan sevda ----------------------------------------------------------------------------- fazilet sevda canan ----------------------------------------------------------------------------- Total number of combination : 6 */ std::vector sVecOne; fcs(sVecOne, 3, rname); std::sort(sVecOne.begin(), sVecOne.end(), f); print(sVecOne); int counter = 0; do { print(sVecOne); ++counter; } while(std::next_permutation(sVecOne.begin(), sVecOne.end(), f)); std::cout << "Total number of combination : " << counter << "\n"; return 0; } Son olarak 'equality' ve 'equivalance' kavramlarına değinelim. C++ dilinde farklı anlamlarda kullanılmaktadır. Bu kavramlardan, >>>>> 'equality' : Karşılaştırma '==' operatörü ile yapıldığındaki durumdur. Bu işlemi yapan fonksiyon nesnesi('functor') ise 'std::equal_to()' şeklindedir. >>>>> 'equivalance' : Karşılaştırma '==' operatörü ile yapılmamaktadır. Velevki varsayılan karşılaştırma operatörü olarak 'std::less' kullanılsın; karşılaştırma "!(x < y) && !(y < x)" kriterine göre yapılacaktır. >> STL kütüphanesindeki genel konvensiyonlar; >>> Yukarıdaki örnekteki isimlendirme konvensiyonuna çoğu yerde rastlanmaktadır. >>> Bir yere yazım işlemi yapan algoritmalar, örneğin yukarıda incelediğimiz 'Copy', geri dönüş değeri olarak HER ZAMAN en son yazdıkları konumdan bir sonraki konumu DÖNDÜRMEKTEDİR. >>> Parametre sırasında okuma amacı ile kullanılacak, yani 'source' olarak kullanılacak, 'range' her zaman hedef 'range' ten, yani 'destination' olarak kullanılan, DAHA ÖNCEDİR. Tıpkı yukarıdaki örnekte olduğu gibi. >>> Yukarıdaki örnekte kullanılan kopyalama fonksiyonu gibi STL kütüphanesindeki çoğu fonksiyonun son parametresine hedef 'range' için başlangıç konumunun bilgisini vermekteyiz. Dolayısıyla hedef 'range' içerisinde, en az 'source' olarak kullanılan 'range' kadar öğe olmasından, BİZ SORUMLUYUZ. 30 elemanlı bir 'range' de bulunan elemanları 15 elemanlı bir 'range' içerisine KOPYALAMA YAPILMADIĞINDAN BİZ SORUMLUYUZ. >>> Algoritmalar iteratör parametreli olduğu için farklı kaplar arasında da işlem yapma olanağı vermektedir. Örneğin 'vector' kabından 'list' kabına kopyalama için. * Örnek 1, #include #include #include /* // Derleyicinin yazacağı fonksiyonun temsili gösterimi: std::list::iterator Copy( std::vector::iterator beg, std::vector::iterator end, std::list::iterator destbeg ) { while(beg != end) *destbeg++ = *beg++; return destbeg; } */ int main() { /* # OUTPUT # Vector => 12 67 90 23 77 List => 0 0 0 0 0 List => 12 67 90 23 77 */ std::vector ivec{ 12, 67, 90, 23, 77 }; std::cout << "Vector => "; for(auto n : ivec) std::cout << n << " "; std::cout << "\n"; std::cout << "List => "; std::list ilist(5); for(auto n : ilist) std::cout << n << " "; std::cout << "\n"; std::copy(ivec.begin(), ivec.end(), ilist.begin()); std::cout << "List => "; for(auto n : ilist) std::cout << n << " "; } >>> Bütün arama algoritmaları, aranan değerin bulunamaması durumunda kendisine geçilen 'range' in '.end()' konumunu döndürmektedir. * Örnek 1, //.. int main() { std::vector> sVec; fc(sVec, 20, rname); pc(sVec); std::string nameToLookUp{"efe"}; if(auto iter = find(sVec.begin(), sVec.end(), nameToLookUp); iter == sVec.end()) std::cout << "Aranılan [" << nameToLookUp << "] ismi BULUNAMADI.\n"; else std::cout << "Aranılan isim bulundu => {" << *iter << "}.\n"; /* # OUTPUT # turgut askin nihal sumeyye akin sezai bora izzet kayahan nurdan nihal necmettin naci taner naz sadi polat seyhan azmi izzet ----------------------------------------------------------------------------- Aranılan [efe] ismi BULUNAMADI. */ nameToLookUp = "haluk"; /* # OUTPUT # turgut haluk nefes handesu polat birhan aydan anil atil yilmaz naz sadi polat seyhan azmi izzet ----------------------------------------------------------------------------- Aranılan isim bulundu => {haluk}. */ } >> STL kütüphanesine Modern C++ ile global 'begin()' ve 'end()' fonksiyonları eklendi. Dolayısıla kaplardaki '.begin()' ve '.end()' fonksiyonlarına alternatif olarak kullanabiliriz. Yine bu global fonksiyonlara 'C-Array' de argüman olarak geçebiliriz. * Örnek 1, //.. int main() { std::deque myDeq; fc(myDeq, 20, Date::random); // 'Date' sınıfının '.random()' fonksiyonu kullanılarak ilgili kap dolduruldu. std::sort(myDeq.begin(), myDeq.end()); // Version-I sort(begin(myDeq), end(myDeq)); // Version-II : ADL mekanizması devreye girmiştir. Argümanlar bir isim alanında tanımlanmış iseler, // isim arama o isim alanında da yapılır. } >> Şimdi de sıra 'STL' içerisindeki 'container' sınıflarının sahip olduğu ortak fonksiyonları incelemeye başlayacağız: >>> Bütün 'container' sınıf şablonlarının 'Default Ctor.' fonksiyonları, içinde öğe tutmayan bir kap nesnesi hayata getirmektedir. * Örnek 1, //.. int main() { std::vector iVecOne; // Öğesi olmayan bir kap nesnesi hayata geldi. std::vector iVecTwo{}; // Öğesi olmayan bir kap nesnesi hayata geldi. std::vector iVecThree(); // 'Most Vexing Parse' gereği bu bir fonksiyon BİLDİRİMİDİR. std::cout << "[" << iVecOne.size() << "]\n"; // OUTPUT => [0] std::cout << "[" << iVecTwo.size() << "]\n"; // OUTPUT => [0] // std::cout << "[" << iVecThree.size() << "]\n"; // error: request for member ‘size’ in ‘iVecThree’, which is of non-class type ‘std::vector()’ } >>> Bütün 'container' sınıf şablonlarının '.size()' fonksiyonun geri dönüş değeri, ilgili kabın 'nested-type' ı olan 'size_type' sınıf türündendir. Fakat bu 'size_t' isminin eş ismi. Kaptaki öğe sayısını döndürmektedir. Sadece 'forward_list' sınıfının bu fonksiyonu yoktur. >>> Bütün 'container' sınıf şablonlarının '.empty()' fonksiyonu, "Kap boş mu?" sorusuna cevap vermektedir. 'constant-time' garantisi vermektedir. >>> Bütün 'container' sınıf şablonlarının 'Copy Ctor.' ve 'Copy Assigning' fonksiyonları mevcuttur. Aynı türden öğeler ve aynı eleman sayısına sahip olmalası gerekiyor. * Örnek 1, //.. int main() { std::vector x{ 1, 3, 5, 23, 78 }; std::vector xx{x}; std::vector xxx(xx); std::vector xxxx = xxx; std::vector y(x); // SENTAKS HATASI. std::list z{ 1, 3, 5, 23, 78 }; std::vector xz{ z }; // SENTAKS HATASI. } >>> Bütün 'container' sınıf şablonlarının '.push_back()' fonksiyonu, kaba sondan ekleme yapan bir fonksiyondur. İki adet 'overload' versiyonu vardır. Birisi 'L-value expression' geçtiğimiz zaman çağrılacak ki bu durumda bizim ifademiz kopyalanacaktır, diğeri ise 'R-Value expression' geçtiğimiz zaman çağrılacaktır. Bu durumda da bizim ifademiz ÇALINACAKTIR. * Örnek 1, //.. int main() { std::vector nameList; std::string name{ "ali" }; // İlgili 'rname()' fonksiyonu sağ taraf ifadesi döndürdüğünden, // 'Move Semantics' mekanizması devreye girecektir. for(int i = 0; i < 5; ++i) nameList.push_back(rname()); // İlgili 'name' isimli değişken sol taraf ifadesi olduğundan, // 'Copy Semantics' mekanizması devreye girecektir. for(int i = 0; i < 5; ++i) nameList.push_back(name); } >>> Bütün 'container' sınıf şablonlarının '.insert()' fonksiyonu belirli bir konuma ekleme yapmak için kullanılır. İlk argüman hedef konum bilgisidir. Geri döndürdüğü değer, ekleme yapılmış konum bilgisidir. * Örnek 1, //.. int main() { /* # OUTPUT # ali | nur | eda | ata | ----------------------------------------------------------------------------- XXX | ali | nur | eda | ata | ----------------------------------------------------------------------------- XXX | YYY | ali | nur | eda | ata | ----------------------------------------------------------------------------- XXX | YYY | ali | nur | eda | ata | ZZZ | ----------------------------------------------------------------------------- XXX | YYY | ali | nur | eda | ata | QQQ | ZZZ | ----------------------------------------------------------------------------- */ std::vector nameList{ "ali", "nur", "eda", "ata" }; pc(nameList, " | "); nameList.insert(nameList.begin(), "XXX"); pc(nameList, " | "); nameList.insert(nameList.begin() + 1, "YYY"); pc(nameList, " | "); nameList.insert(nameList.end(), "ZZZ"); pc(nameList, " | "); nameList.insert(nameList.end() - 1, "QQQ"); pc(nameList, " | "); } * Örnek 2, //.. int main() { /* # OUTPUT # ali | nur | eda | ata | ----------------------------------------------------------------------------- AAA | BBB | CCC | DDD | ----------------------------------------------------------------------------- [2] => ali | nur | AAA | BBB | CCC | DDD | eda | ata | ----------------------------------------------------------------------------- [0] => EEE | FFF | GGG | HHH | ali | nur | AAA | BBB | CCC | DDD | eda | ata | ----------------------------------------------------------------------------- [12] => EEE | FFF | GGG | HHH | ali | nur | AAA | BBB | CCC | DDD | eda | ata | IIII | IIII | IIII | IIII | ----------------------------------------------------------------------------- */ std::vector nameList { "ali", "nur", "eda", "ata" }; pc(nameList, " | "); std::list nameListTwo{ "AAA", "BBB", "CCC", "DDD" }; pc(nameListTwo, " | "); std::cout << "[" << std::distance( nameList.begin(), nameList.insert(nameList.begin() + 2, nameListTwo.begin(), nameListTwo.end()) ) << "] => "; pc(nameList, " | "); std::cout << "[" << std::distance( nameList.begin(), nameList.insert(nameList.begin(), { "EEE" , "FFF", "GGG", "HHH" }) ) << "] => "; pc(nameList, " | "); std::cout << "[" << std::distance( nameList.begin(), nameList.insert(nameList.end(), 4, "IIII") ) << "] => "; pc(nameList, " | "); } >>> '.emplace()' fonksiyonları: Eklemenin yapılacağı bellek alanını 'this' göstericisi olarak kullanıp, yerinde bir nesne hayata getirmektedir. Yani ne kopyalama semantiği ile ne de taşıma semantiği ile bir nesne hayata getirilmemektedir. Direkt olarak nokta atışı, o noktada nesne hayata getiriliyor. * Örnek 1, //.. int main() { /* # OUTPUT # ali | nur | eda | ata | ----------------------------------------------------------------------------- ali | nur | eda | ata | muhittin | ----------------------------------------------------------------------------- ali | nur | eda | ata | muhittin | muhittin | ----------------------------------------------------------------------------- ali | nur | eda | ata | muhittin | muhittin | AAAAAAAAAAAAAAAAAAAA | ----------------------------------------------------------------------------- */ std::vector nameList{ "ali", "nur", "eda", "ata" }; pc(nameList, " | "); std::string name{"muhittin"}; nameList.push_back(name); // Copy Ctor. pc(nameList, " | "); nameList.push_back(std::move(name)); // Move Ctor. pc(nameList, " | "); nameList.emplace_back(20, 'A'); // Doğrudan hedef adreste nesne oluşturulacak. Dolayısıyla 'std::string' sınıfının 'Ctor.' fonksiyonlarından // birisine çağrı yapmamız gerekiyor. 'forwarding reference' mekanizması ile 'emplace_back' fonksiyonuna // geçilen argümanlar direkt olarak 'Ctor.' çağrısında kullanılacaktır. Bu satırda ne 'Move Ctor.' ne de // 'Copy Ctor.' çağrıldı. pc(nameList, " | "); } * Örnek 2, //.. int main() { /* # OUTPUT # 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | ----------------------------------------------------------------------------- 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | 11 Nisan 2022 Pazartesi | ----------------------------------------------------------------------------- 17 Eylul 1993 Cuma | 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | 01 Ocak 1900 Pazartesi | 11 Nisan 2022 Pazartesi | ----------------------------------------------------------------------------- */ std::vector dVec(3); pc(dVec, " | "); dVec.emplace_back(11, 4, 2022); pc(dVec, " | "); dVec.emplace(dVec.begin(), 17, 9, 1993); pc(dVec, " | "); } >>> Gerçek silme yapan fonksiyonlar: Bir tanesi bir konum bilgisi alır ve o konumdakini siler. Diğeri ise bir 'range' alır ve o 'range' içerisindeki öğeleri siler. Geri dönüş değeri silme işleminden sonra kalan ilk öğenin konum bilgisidir. * Örnek 1, Direkt olarak konum bilgisi verirsek: //.. int main() { /* # OUTPUT # amasya | mugla | mersin | trabzon | tokat | eskisehir | nevsehir | nigde | artvin | burdur | ----------------------------------------------------------------------------- [9] => amasya | mugla | mersin | trabzon | tokat | eskisehir | nevsehir | nigde | artvin | ----------------------------------------------------------------------------- [0] => mugla | mersin | trabzon | tokat | eskisehir | nevsehir | nigde | artvin | ----------------------------------------------------------------------------- mugla | mersin | trabzon | tokat | eskisehir | nevsehir | nigde | ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 10, rtown); pc(sVec, " | "); // Son konumdaki gitti. std::cout << "[" << std::distance(sVec.begin(), sVec.erase(sVec.end() - 1)) << "] => "; pc(sVec, " | "); // İlk konumdaki gitti. std::cout << "[" << std::distance(sVec.begin(), sVec.erase(sVec.begin())) << "] => "; pc(sVec, " | "); sVec.pop_back(); // Son konumdaki gitti. pc(sVec, " | "); } * Örnek 2, Bir aralık verilmesi durumunda //.. int main() { /* # OUTPUT # bitlis | mersin | tunceli | bartin | antalya | sinop | karaman | tekirdag | kirsehir | van | ----------------------------------------------------------------------------- [1] => bitlis | van | ----------------------------------------------------------------------------- */ std::vector sVec; fc(sVec, 10, rtown); pc(sVec, " | "); // İlk konumdaki ve son konumdakiler hariç, aradaki hepsi gitti. std::cout << "[" << std::distance(sVec.begin(), sVec.erase(sVec.begin() + 1, sVec.end() - 1)) << "] => "; pc(sVec, " | "); } >>> İki farklı sınıf şablonundan nesneyi, 'iterator' parametreli 'Ctor.' fonksiyonlarına çağrı yaparak, birlikte kullanabiliriz. * Örnek 1, //.. int main() { std::list myList{ 1, 3, 5, 23, 78 }; // std::vector myVec{ myList }; // SENTAKS HATASI. std::vector myVec{ myList.begin(), myList.end() }; // İş bu vektör nesnesini, 'myList' sınıfının öğeleri ile hayata getirdik. // std::vector myVecTwo{ myList }; // SENTAKS HATASI. // İteratör konumundaki nesneler birbirine atanabilir olduğu sürece, ilgili kaplarda tutulan öğelerin türleri // de önemsizdir. İş bu vektör nesnesini, 'myList' sınıfının öğeleri ile hayata getirdik. std::vector myVecTwo{ myList.begin(), myList.end() }; // C-array de kullanabiliriz. const char* nameList[] = { "enes", "gokhan", "canibek", "necati" }; std::vector myVecThree{ std::begin(nameList), std::end(nameList) }; } >>> 'Sequence Container' grubundaki sınıf şablonlarınım 'size_t' parametreli 'Ctor.' fonksiyonları, iş bu kap nesnesini, bu 'm' kadar 'default init.' edilmiş öğe ile hayata başlatmaktadır. * Örnek 1, //.. int main() { std::vector iVec(10); // İlgili 'iVec' nesnesi 'default init.' edilmiş 10 öğe tutmaktadır. } * Örnek 2, Kapta tutulan öğeler bir sınıf türünden olması durumunda, bu öğelerin 'Default Constructable' olmaları gerekiyor. //.. class Data { public: Data(int); // 'Parametreli Ctor.' olduğundan 'Default Ctor.' YOKTUR. }; int main() { std::vector dVec(20); // Sentaks hatası. } >>> 'Sequence Container' grubundaki ortak fonksiyonlar '.back()' ve '.front' isimli fonksiyonlar, kaplardaki son ve ilk öğeye erişim sağlamaktadır. Yine bu fonksiyonların 'const' 'overload' edilmiş versiyonları da vardır. İlgili kapların boş olması durumunda bu iki fonksiyonun çağrılması bir HATA GÖNDERİLMESİNE neden olmaz. >>> 'Sequence Container' grubundaki sınıf şablonlarının '.resize()' isimli bir üye fonksiyonu vardır. '.size()' büyüklüğünü değiştirmektedir. Dolayısıyla sondan ekleme yoluyla yeni öğeler eklendi. * Örnek 1, //.. int main() { /* # OUTPUT # size = 6, capacity = 6 => 10 20 30 40 56 90 ----------------------------------------------------------------------------- size = 10, capacity = 12 => 10 20 30 40 56 90 0 0 0 0 ----------------------------------------------------------------------------- */ std::vector iVec{ 10, 20, 30, 40, 56, 90 }; std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); iVec.resize(10); // Yeni eklenen öğeler '0' değerinde. std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); } * Örnek 2, ilgili fonksiyonun bir diğer 'overload' versiyonu ise ikinci bir argüman almaktadır. Yeni eklenecek öğeleri bu argüman ile hayata getirmektedir. //.. int main() { /* # OUTPUT # size = 6, capacity = 6 => 10 20 30 40 56 90 ----------------------------------------------------------------------------- size = 15, capacity = 15 => 10 20 30 40 56 90 31 31 31 31 31 31 31 31 31 ----------------------------------------------------------------------------- */ std::vector iVec{ 10, 20, 30, 40, 56, 90 }; std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); iVec.resize(15, 31); // Yeni eklenen öğeler '31' değerinde. std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); } * Örnek 3, ilgili fonksiyona '.size()' boyutundan küçük bir değer argüman olarak geçilirse kabın sonundan başlayarak bir GERÇEK SİLME işlemi gerçekleştirecektir. //.. int main() { /* # OUTPUT # size = 6, capacity = 6 => 10 20 30 40 56 90 ----------------------------------------------------------------------------- size = 15, capacity = 15 => 10 20 30 40 56 90 31 31 31 31 31 31 31 31 31 ----------------------------------------------------------------------------- size = 6, capacity = 15 => 10 20 30 40 56 90 ----------------------------------------------------------------------------- */ std::vector iVec{ 10, 20, 30, 40, 56, 90 }; std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); iVec.resize(15, 31); // Yeni eklenen öğeler '31' değerinde. std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); iVec.resize(6); std::cout << "size = " << iVec.size() << ", capacity = " << iVec.capacity() << " => "; pc(iVec); } >> Şimdi de ilgili 'container' sınıflarını kendi özelinde incelemeye, 'std::vector' sınıfı ile başlayalım: >>> 'std::vector' : Eğer sondan ekleme yapıyorsak 'constant-time', 'std::list' de ise hangi noktadan yapıyorsam yapayım 'constant-time'. Yine karmaşıklık açısından sırasıyla 'O(n)' ve 'O(1)' karmaşıklığında. Fakat zaman içerisinde bu sınınfa o kadar güzel implementasyonlar yapılmış ki belli bir büyüklüğe kadar 'std::vector' sınıfı, 'std::list' sınıfından daha verimli çalışmaktadır. 'std::vector' de bir sınıf şablonudur. * Örnek 1, //.. // Temsili implementasyonu: template class CustomAllocator; // Temsili implementasyon: template> class Vector{}; // Temsili implementasyon: template using CustomVec = std::Vector>; int main() { std::Vector iVec; // İlgili fonksiyon şablonunun; // i. Birinci parametresi, ki bu durumda 'T' ye karşılık gelecek tür, kapta tutulacak öğelerin tür // bilgisidir. // ii. İkinci parametresi, ki bu durumda 'A' ye karşılık gelecek tür, dinamik bellek alanı ihtiyacını // karşılayan bir sınıftır. Bu sınıf ayrıca o bellek alanında nesnelerin oluşturulması ve yok // edilmesinden de sorumludurlar. Varsayılan değer olarak 'std::allocator' sınıf şablonu kullanılır. std::Vector> myCustomVec; // Görüldüğü üzere 'allocator' olarak bizim 'custom' allocator sınıfımız kullanıldı. // Uzun uzun yazmak yerine aşağıdaki gibi bir 'template alias' da kullanabilirdik; CustomVec myCustomVecTwo; } >>>> Dinamik dizi veri yapısını implemente etmektedir. Kapasite dolduğunda, yeni bir ekleme yapılırsa, ilgili bellek alanı başka bir yere taşınıyor eğer halihazırdaki konumun devamında yeteri kadar yer yok ise. >>>> Modern C++ ile dile 'initializer_list' parametre türünden bir 'Ctor.' eklenmiştir. >>>>> 'initializer_list' hatırlatması: Üye fonksiyon olarak sadece '.begin()', '.end()' ve '.size()' fonksiyonu vardır. Fakat bu fonksiyonların 'global function' eşleniklerine, bu sınıf türünü argüman olarak geçebiliriz. * Örnek 1, //.. int main() { /* # OUTPUT # [4] => ali can veli ahmet */ std::initializer_list nameList{ "ali", "can", "veli", "ahmet" }; std::cout << "[" << nameList.size() << "] => "; std::copy(nameList.begin(), nameList.end(), std::ostream_iterator{std::cout, " "}); // Bu sınıf türünden iteratörleri salt okuma amaçlı kullanabiliriz. auto iter = nameList.begin(); *(++iter) = "_"; // error: no match for ‘operator=’ // (operand types are ‘const std::__cxx11::basic_string’ and ‘const char [2]’) } * Örnek 2, Tür çıkarımını bu sınıf türünden de yaptırabiliriz. Tek istisna, fonksiyon şablonlarında. //.. template void func(T x) {} int main() { auto x = { 1 }; // Tür çıkarımı 'std::initializer_list' auto y{ 10 }; // Artık Modern C++ ile burada tür çıkarımı 'int' şeklinde. auto xx = { 1.1, 2.2 }; // Tür çıkarımı 'std::initializer_list' auto yy{ 10, 20 }; // Sentaks hatası oluşuyor. // İstisna olan kısım burası. Argüman olarak virgüller ile ayrılan bir liste gönderildiğinde tür // çıkarımı yapılamıyor ve sentaks hatası oluşuyor. func({ 1, 2, 3, 4 }); } * Örnek 3, En çok 'Ctor.' fonksiyonlarının parametre olarak kullanılırlar. //.. class Myclass{ public: Myclass(int) { std::cout << "1\n"; } Myclass(std::initializer_list) { std::cout << "2\n"; } }; int main() { Myclass m(10); // '1' numaralı çağrılacaktır. Myclass mm{10}; // '2' numaralı çağrılacaktır. Çünkü 'Function Overload Resolution' aşamasında ilgili 'overload' // versiyonun seçilebilirliği daha yüksektir. } * Örnek 4, Bazı sınıf şablonlarında 'size_t' parametreli 'Ctor.' ile birlikte kullanılır. BU NOKTAYA DİKKAT EDİLMESİ GEREKİYOR. //.. int main() { std::string s(10, 'A'); // 10 adet öğe içermektedir ve her birisinin değeri 'A' karakteridir. std::string s{48, 'A'}; // 2 adet öğe içermektedir. İlk öğe 48 kodlu karakter, ki '0' karakterine karşılık gelmektedir. // İkinci öğe ise 'A' karakteridir. std::vector x(10); // 10 tane öğe var, hepsi de '0' ile hayata gelik. // Çünkü burada 'size_t' parametreli 'Ctor.' çağrıldı. std::vector y{10}; // 1 tane öğe var, '10' ile hayata gelik. // Çünkü burada 'std::initializer_list' parametreli 'Ctor.' çağrıldı. std::vector x(10, 20); // 10 tane öğe var, hepsi de '20' ile hayata gelikler. // Çünkü burada 'size_t' parametreli 'Ctor.' çağrıldı. std::vector y{10, 20}; // 2 tane öğe var, '10' ve '20' ile hayata gelikler. // Çünkü burada 'std::initializer_list' parametreli 'Ctor.' çağrıldı. } * Örnek 5, //.. class Summer{ public: Summer(std::initializer_list list) { int sum = 0; for(auto index : list) sum += index; m_average = static_cast(sum) / list.size(); } double getAverate()const { return m_average; } private: double m_average; }; int main() { Summer mx{ 2, 7, 9, 5, 3 }; std::cout << "Average : " << mx.getAverate(); // OUTPUT => Average : 5.2 } * Örnek 6, //.. void func(std::initializer_list list) { /*...*/ } int main() { auto myList = { 1, 3, 7, 12 }; func(myList); // BURADA BİR KOPYALAMA YOKTUR. ARKA PLANDA BİR 'const' BİR DİZİ OLUŞTURULMAKTADIR VE ADRES // BİLGİSİ GEÇİLMEKTEDİR. Fakat yine de ilgili fonksiyonun imzasında '&' deklaratörü // kullanabiliriz. } * Örnek 7, //.. int main() { /* # OUTPUT # 2 | 4 | 6 | 8 | 10 | ----------------------------------------------------------------------------- 2 | -1 | -2 | -3 | -4 | 4 | 6 | 8 | 10 | ----------------------------------------------------------------------------- */ std::vector iVec{ 2, 4, 6, 8, 10 }; pc(iVec, " | "); iVec.insert ( std::next(iVec.begin()), { -1, -2, -3, -4 } ); pc(iVec, " | "); } >>>>> Kapta tutulan öğelerin sınıf türlerinden olması durumunda '{}' kullanılması bir anlam farklılığı OLUŞTURMAYACAKTIR. //.. int main() { Date dx{ 12, 5, 1999 }; std::vector dVec{ 10, dx }; // Tutulan öğeler sınıf türünden oldukları için; kapta 10 tane öğe olacak ve her birinin değeri 'dx' // nesnesi ile aynı olacak. } >>>> Pekiştirici bir örnek: * Örnek 1, //.. int main() { std::list myList{ 12, 53, 78, 95, 65 }; std::vector x; // Default Ctor. std::vector y{x}; // Copy Ctor. std::vector z(10); // 'size_t' Parametreli Ctor. std::vector z(10, 45); // 'size_t' ve 'T' Parametreli Ctor. std::vector a{10, 45}; // 'std::initializer_list' Parametreli Ctor. std::vector b{ myList.begin(), myList.end() }; // 'range' Parametreli Ctor. std::list myVec{ 12, 53, 78, 95, 65 }; std::list myVecTwo{ std::move(myVec) }; // Move Ctor. } * Örnek 2, Bir fonksiyonun geri dönüş değeri 'container' ise tipik olarak otomatik ömürlü bir nesne döndürecektir; bu durumda ya 'Move Semantics' devreye giriyor ya da derleyicinin yaptığı optimizasyonlar sonucu kopyalama maliyetinden kaçınılmış olmaktadır. AMACIMIZ ÇAĞIRAN KODA BİR 'container' İLETMEK İSE AŞAĞIDAKİ ŞEKİLDE RAHATLIKLA YAPABİLİRİZ, MODERN CPP KULLANIYORSAK. //.. std::vector myFunc(void) { std::vector sVec; //.. some code here return sVec; } int main() { auto vec = func(); // Burada kopyalamanın maliyetinden kaçınılmış oluyor, Modern Cpp dönemiyle birlikte. } >>>> 'std::vector' sınıfının arayüzündeki fonksiyonlar: >>>>> İsminde 'begin' ve 'end' içereren bütün fonksiyonlar iteratör veren fonksiyonlardır. >>>>> '.capacity()' fonksiyonu, 'reallocation' gerçekleşmeden evvel, kabın tutabileceği maksimum öğe sayısını döndürmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # [0x5599d3768f70], size / capacity => 20 / 32 12 adedince yeni oge eklenebilir. [0x5599d3768f70], size / capacity => 32 / 32 [0x5599d3769410], size / capacity => 33 / 64 [0x7ffebcfc4ce0], size / capacity => 1033 / 2048 */ std::vector iVec; fc(iVec, 20, rand); std::cout << "[" << &iVec.at(0) << "], size / capacity => " << iVec.size() << " / " << iVec.capacity() << "\n"; auto remainingSize = (iVec.capacity() - iVec.size()); std::cout << remainingSize << " adedince yeni oge eklenebilir.\n"; for (size_t i = 0; i < remainingSize; i++) { iVec.push_back(i); } std::cout << "[" << &iVec.at(0) << "], size / capacity => " << iVec.size() << " / " << iVec.capacity() << "\n"; iVec.push_back(0); std::cout << "[" << &iVec.at(0) << "], size / capacity => " << iVec.size() << " / " << iVec.capacity() << "\n"; for (int i = 0; i < 1'000; i++) { iVec.push_back(i); } std::cout << "[" << &iVec << "], size / capacity => " << iVec.size() << " / " << iVec.capacity() << "\n"; // Çıktıtan da görülebileceği üzere 'reallocation' yapıldıktan sonra öğelerin adresleri // değişmektedir. Bu da beraberinde şöyle bir problemi getirebilir; velevki bizim öğelerimi // gösteren göstericilerimiz, iteratörlerimiz olsun. 'reallocation' sonucunda öğelerin yerleri // değiştiği taktirde bu göstericiler/iteratörler eski adresleri/konumları göstereceklerinden // dolayı artık 'invalid' hale gelecekler. Bu duruma da 'iterator_invalidation' denmektedir. } * Örnek 2, //.. int main() { /* # OUTPUT # 1804289383_846930886_1681692777_1714636915_1957747793_ ----------------------------------------------------------------------------- size / capacity 5 / 8 *iter = 1804289383 3 kadar oge daha eklenebilir... size / capacity 8 / 8 1804289383_846930886_1681692777_1714636915_1957747793_1_1_1_2_ ----------------------------------------------------------------------------- size / capacity 9 / 16 *iter = 0 3 kadar oge daha eklenebilir... */ std::vector iVec; fc(iVec, 5, rand); pc(iVec, "_"); std::cout << "size / capacity\n"; std::cout << std::setw(4) << std::left << iVec.size() << " / " << std::setw(5) << iVec.capacity() << "\n\n"; auto iter = iVec.begin(); std::cout << "*iter = " << *iter << "\n"; auto remainingSize = iVec.capacity() - iVec.size(); std::cout << remainingSize << " kadar oge daha eklenebilir...\n"; for (int i = 0; i < remainingSize; i++) { iVec.push_back(1); } std::cout << "\nsize / capacity\n"; std::cout << std::setw(4) << std::left << iVec.size() << " / " << std::setw(5) << iVec.capacity() << "\n\n"; // Bu çağrı sonucunda 'reallocation' gerçekleşeceğinden, yukarıdaki 'iter' isimli iteratör // artık 'dangling' hale gelmiştir. iVec.push_back(2); for (auto i : iVec) { std::cout << i << "_"; } std::cout << "\n-----------------------------------------------------------------------------\n"; std::cout << "\nsize / capacity\n"; std::cout << std::setw(4) << std::left << iVec.size() << " / " << std::setw(5) << iVec.capacity() << "\n\n"; std::cout << "*iter = " << *iter << "\n"; std::cout << remainingSize << " kadar oge daha eklenebilir...\n"; } >>>>> '.at()' ve '.operator[]' fonksiyonları ile belirli bir indisteki öğelere erişebiliriz. Tıpkı 'std::string' sınıfında olduğu gibi. Bu fonksiyonların geri dönüş değeri 'reference' olduğundan, ilgili indisteki öğeleri de değiştirebiliriz. Geçersiz indis bilgisi geçildiğide sadece '.at()' fonksiyonu bir hata gönderecektir. Diğer fonksiyon ise 'run-time' hatasına neden olmakta. Ek olarak bu iki fonksiyonun da 'const-overload' edilmiş versiyonu vardır. 'const' nesneleri için kullanılmaktadır. Dolayısla artık bu fonksiyonların geri dönüş değeri de 'const-reference' şeklindedir. Bu iki fonksiyon haricinde, 'sequence-container' gruplarda ortak olan '.back()' ve '.front()' fonksiyonları da vardır ki onlar hakkında bilgi aşağıda paylaşılmıştır. Son olarak unutmamalıyız ki 'iterator' kullanarak da bizler kap içerisindeki nesnelere erişim sağlayabiliriz. >>>>>> Bir 'std::vector' ü dolaşmak için üç farklı seçeneğimiz vardır. Bunlar; >>>>>>> '.at()' ve/veya '.operator[]' fonksiyonları ile birlikte C dilinde de olan 'for-loop' kullanmak: * Örnek 1, //.. int main() { std::vector sVec; fc(iVec, 10, rtown); for(size_t i = 0; i < sVec.size(); ++i) { std::cout << sVec.at(i) << " "; // std::cout << sVec[i] << " "; } } >>>>>>> '.at()' ve/veya '.operator[]' fonksiyonları ile birlikte C dilinde de olan 'for-loop' döngüsünü 'iterator' ile birlikte kullanmak: * Örnek 1, //.. int main() { std::vector sVec; fc(iVec, 10, rtown); for(auto iter = sVec.cbegin(); iter != sVec.cend(); ++iter) { std::cout << *iter << " "; } } >>>>>>> 'range-based for-loop' kullanmak: * Örnek 1, //.. int main() { std::vector sVec; fc(iVec, 10, rtown); for(auto name : sVec) std::cout << name << " "; } * Örnek 2, Okuma amaçlı dolaşmak için aşağıdaki yöntemi kullanmalıyız. //.. int main() { std::vector sVec; fc(iVec, 10, rtown); for(const auto& name : sVec) std::cout << name << " "; } * Örnek 3, Değiştirme amacıyla aşağıdaki yöntemi kullanabiliriz. //.. int main() { std::vector sVec; fc(iVec, 10, rtown); for(auto& name : sVec) name += "_can"; } >>>>> '.reserve()' : Bu fonksiyon kapasiteyi rezerve etmektedir. 'size' büyüklüğüne bir etkisi yoktur. Sadece ekstra 'reallocation' yapılmasının önüne geçmektedir. Kapasiteyi bu fonksiyon ile düşürmek ise derleyiciye bağlı bir durumdur. Negatif değer argüman olarak geçilirse bir hata gönderecektir. * Örnek 1, //.. int main() { std::vector iVec; ivec.reserve(100); // Kapasite büyüklüğünü bir şekilde ön gördüğümüzü varsayar isek 'reallocation' adedini // azaltmış oluruz. std::cout << "size = " << iVec.size() << "\n"; std::cout << "capacity = " << iVec.capacity() << "\n"; } * Örnek 2, //.. int main() { /* # OUTPUT # 1. allocation happened. size = 11, capacity = 20 2. allocation happened. size = 21, capacity = 40 3. allocation happened. size = 41, capacity = 80 4. allocation happened. size = 81, capacity = 160 5. allocation happened. size = 161, capacity = 320 6. allocation happened. size = 321, capacity = 640 7. allocation happened. size = 641, capacity = 1280 8. allocation happened. size = 1281, capacity = 2560 9. allocation happened. size = 2561, capacity = 5120 10. allocation happened. size = 5121, capacity = 10240 ... */ std::vector iVec(10); auto lastCapacity = iVec.capacity(); int allocationCounter{}; for(;;) { iVec.push_back(0); auto currentCapacity = iVec.capacity(); if( currentCapacity > lastCapacity ) { std::cout << ++allocationCounter << ". allocation happened. size = " << iVec.size() << ", capacity = " << iVec.capacity(); lastCapacity = currentCapacity; (void)getchar(); } } } * Örnek 3, int main() { /* # OUTPUT # 1. allocation happened. size = 10001, capacity = 20000 ... */ std::vector iVec(10); iVec.reserve(10000); auto lastCapacity = iVec.capacity(); int allocationCounter{}; for(;;) { iVec.push_back(0); auto currentCapacity = iVec.capacity(); if( currentCapacity > lastCapacity ) { std::cout << ++allocationCounter << ". allocation happened. size = " << iVec.size() << ", capacity = " << iVec.capacity(); lastCapacity = currentCapacity; (void)getchar(); } } } * Örnek 4, //.. int main() { /* # OUTPUT # size = 100, capacity = 1000 size = 100, capacity = 1000 */ std::vector iVec(100); iVec.reserve(1000); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; iVec.reserve(200); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity(); } >>>>> '.shrink_to_fit()' : Modern Cpp ile dile eklenen bir fonksiyondur. Fazla kapasiteyi büzmek için kullanılır. * Örnek 1, //.. int main() { /* # OUTPUT # size = 10000, capacity = 16384 size = 1, capacity = 16384 size = 1, capacity = 1 */ std::vector iVec; for(int i = 0; i < 10'000; ++i) iVec.push_back(i); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; // Kapta bir öğe kalıncaya kadar bütün öğeler silindi. iVec.erase(std::next(iVec.begin()), iVec.end()); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; iVec.shrink_to_fit(); // Fazlalık kapasite miktarı küçültüldü. std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; } * Örnek 2, 'swap-trick' hilesi: Artık bu hileyi kullanmaya gerek kalmadı. //.. int main() { /* # OUTPUT # size = 10000, capacity = 16384 size = 1, capacity = 16384 size = 1, capacity = 1 */ std::vector iVec; for(int i = 0; i < 10'000; ++i) iVec.push_back(i); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; // Kapta bir öğe kalıncaya kadar bütün öğeler silindi. iVec.erase(std::next(iVec.begin()), iVec.end()); std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; std::vector{iVec}.swap(iVec); // swap-trick : 'iVec' nesnesi ile bir Geçici Nesne oluşturuyoruz. Oluşturulan bu geçici // nesnenin '.size()' büyüklüğü aynı kalmakta fakat '.capacity()' büyüklüğü, '.size()' // büyüklüğüne uygun olmakta. Oluşturulan bu geçici nesne üzerinden '.swap()' fonksiyonu // çağrılıyor ve argüman olarak da bizim orjinal 'iVec' isimli sınıf nesnemiz kullanılıyor. // Kaptaki öğeler değişmezken, '.capacity()' bilgisi 'swap' ediliyor. Böylelikle geçici // nesnenin '.capacity()' bilgisi bizim has sınıf nesnemize geçmiş oluyor. std::cout << " size = " << iVec.size() << ", capacity = " << iVec.capacity() << "\n"; } >>>>> 'std::vector' sınıfında 'push_front' fonksiyonu YOKTUR. >>>>> Bir 'std::vector' içerisindeki bütün öğeleri silmek için neler yapabiliriz? El-cevab : Konteynırın '.clear()' fonksiyonunu kullanmalıyız. * Örnek 1, //.. int main() { std::vector sVec; fc(sVec, 50, rname); pc(sVec); // sVec.clear(); // I // sVec.resize(0); // II // sVec.erase(sVec.begin(), sVec.end()); // III : Kapasite büzülmeyecektir. // sVec = std::vector{}; // IIII : Kapasite büzülecektir. } >>>>> '.max_size()' fonksiyonu : Tutabileceği maksimum öğe sayısıdır. 'static' üye fonksiyon DEĞİLDİR. Çünkü kullanılan 'std::allocator' sınıfından ötürü maksimum kapasite değişebilir. >>>>> Atama işlemini gerçekleştiren fonksiyonlar : >>>>>> '.operator=()' fonksiyonları hem 'Copy Semantics' hem de 'Move Semantics' için destek vermektedir. 'initializer-list' için de destek vermektedir. * Örnek 1, //.. int main() { std::vector a{ 1, 2, 3, 4 }; std::vector b; b = a; // b.operator(a); // Copy Ctor. b = std::move(a); // b.operator(std::move(a)); // Move Ctor. a = { 4, 3, 2, 1 }; // 'Init. List' } >>>>>> '.assign()' fonksiyonu üç adet 'overload' barındırmaktadır. Bunlar 'fill-assign', 'range-assign' ve 'initializer-list-assign'. Geri dönüş değeri yoktur. * Örnek 1, //.. int main() { std::vector a{ 1, 2, 3, 4 }; std::vector b; b.assign(10, 56); // İlgili kap 10 adet '56' değerinde öğeye sahiptir. // İlgili kap '{}' arasındaki değerlere sahip öğelerden oluşmaktadır. b.assign( { 4, 3, 2, 1 } ); std::deque c{ 10, 11, 12, 13 }; b.assign( c.begin(), c.end() ); // İlgili kap, 'c' kabının öğeleri ile hayata gelmiştir. } >>>>>> '.swap()' fonksiyonu kapların içerisinde bulunan 'göstericileri' birbirleri ile değiştirmektedir. Kaplardaki öğeler birbiri ile yer değiştirmemektedir. * Örnek 1, //.. int main() { std::vector a{ 1, 2, 3, 4 }; std::vector b{ 11, 22, 33, 44 }; a.swap(b); // swap(a, b); } >>>>> '.clear()' : Kaptaki bütün öğeleri silmektedir. >>>>> '.data()' : Arkadaki dinamik bellek bloğunun adresini döndürmektedir. C API leri ile birlikte kullanıldığında çok işe yaramaktadır. Geri dönüş değeri 'T*' veya 'const T*' türünden bir gösterici. * Örnek 1, //.. void print_array(const int* p, size_t size) { while(size--) printf("%d", *p++); printf("\n-----------------------\n"); } int main() { std::vector myVec{ 1, 3, 5, 7, 9 }; printf_array(myVec.data(), myVec.size()); // &myVec[0]; // data(myVec); // C++17 // &*myVec.begin(); // UNUTULMAMALIDIR Kİ GERİ DÖNÜŞ DEĞERİNİ BİR GÖSTERİCİDE SAKLARSAK, 'reallocation' SONRASINDA İLGİLİ // GÖSTERİCİ 'Dangling Pointer' HALİNE GELECEKTİR. } >>>>> '.get_allocator()' : İlgili kapta kullanılan 'allocator' sınıfını 'get' etmektedir. >>>> 'std::string' sınıfındaki 'Small Buffer Optimization' bu sınıfta MEVCUT DEĞİLDİR. >>> 'std::deque' sınıf şablonunun incelenmesi : Dinamik dizilerin dizisi manasındadır. Amaç indeks ile sabit zamanda erişmek, hem baştan hem sondan eklemeyi 'constant-time' yapmak. Vektörün vektör açılımı şeklinde de örneklendirebiliriz. 'reallocation' problemi yoktur. Kap dolduğunda yeni bir dizi daha eklenecektir. Fakat bu yeni eklenen dizi ile önceki dizilerin peşpeşe olma garantisi yoktur. Vektör sınıfı ile benzer parametre yapısına ve 'interface' fonksiyonlarına sahiptirler. * Örnek 1, //.. int main() { /* # OUTPUT # 994 ----------------------------------------------------------------------------- 114 994 ----------------------------------------------------------------------------- 518 114 994 ----------------------------------------------------------------------------- 38 518 114 994 ----------------------------------------------------------------------------- 38 518 114 994 843 ----------------------------------------------------------------------------- 38 518 114 994 843 415 ----------------------------------------------------------------------------- 38 518 114 994 843 415 235 ----------------------------------------------------------------------------- 112 38 518 114 994 843 415 235 ----------------------------------------------------------------------------- 62 112 38 518 114 994 843 415 235 ----------------------------------------------------------------------------- 62 112 38 518 114 994 843 415 235 687 ----------------------------------------------------------------------------- */ std::deque myDeque; Irand rand{ 0, 1000 }; for(int i = 0; i < 10; ++i) { auto value = rand(); if(value % 2 == 0) myDeque.push_front(value); else myDeque.push_back(value); print(myDeque); } } >>>> 'std::vector' sınıfında olmayan ama 'std::deque' sınıfında olan fonksiyonlar: '.emplace_front()', '.pop_front()', '.push_front()' fonksiyonlarından oluşmaktadır. >>>> 'reallocation' meydana gelmemektedir. >>>> 'iterator invalidation' kuralları, 'std::vector' sınıfındakinden tamamiyle farklıdır. İş bu kurallar; Baştan ve sondan değil ama aradaki konumlardan yapılan ekleme işlemleri, ilgili kaptaki öğeleri gösteren bütün iteratörleri, referansları ve göstericileri 'invalid' durumuna getirir. Eğer her iki uçtan birine yapılan ekleme yapılırsa, kaptaki öğeleri gösteren bütün iteratörleri 'invalid' durumuna durumuna gelirken referans ve göstericileri ETKİLEMEMEKTEDİR. Eğer silme işlemi yaparsak ve bu işlem sondan gerçeleşir ise sadece son konumu gösteren iteratör ile silinen öğeyi gösteren referans ve göstericiler 'invalid' durumuna geçerler. Eğer silme işlemini ilk öğe için yaparsak, ki bu ilk öğenin son öğe olmaması lazım, silinen öğeyi gösteren iteratörler, referanslar ve göstericiler 'invalid' olurlar. >>>> 'std::vector' sınıfının 'bool' açılımı 'bool' türden değişkenler tutmamaktadır fakat 'std::deque' sınıfının 'bool' açılımı gerçekten de 'bool' tutmaktadır. >>> Bağlı liste veri yapıları: 'std::list' ve 'std::forward_list' sınıf şablonlarından meydana gelmektedir. Konumu bilinen her yerden ekleme ve silme işlemleri 'constant-time'. 'swap' işleminin yoğun yapıldığı ve öğelerin de bir hayli büyük olduğu bir 'range' için liste sınıf şablonu kullanılabilir. Çünkü bu tip veri yapılarında verinin kendisi değil, düğümleri gösteren göstericiler 'swap' edilmektedir. Ayrıca düğümler hangi listeye ait olduklarını bilmezler. Yani bir düğümü halihazırda bulunduğu listeden çıkartıp bir başka listeye ekleyebiliyoruz. Örneğin, iki vektör arasında bir nesne transferi gerçekleştirmek isteyelim. İlk önce kaynak vektördeki ilgili nesnenin hayatı sonlandırılıyor. Sonrasında onun bulunduğu vektör tekrardan 'allocate' ediliyor, boşalan yeri doldurmak için. Devamında ise hedef vektörde bir yer açılıyor ve nesnemiz o konumda hayata geliyor. Fakat liste kullansaydık sadece göstericileri 'swap' yaparak öğeleri takas yapabilirdik. ÜStelik ilgili nesne için 'Ctor.' ve 'Dtor.' fonksiyonlarının çağrılmasına, ilgili liste için tekrardan yer açılmasına da gerek kalmayacaktı. Bu sınıflara ait iteratörler 'bi-directional iterator' grubundandır. >>> 'std::list' sınıfının incelenmesi : Asla 'std::unique()', 'std::reverse()', 'std::merge()' gibi algoritmaları asla kullanmamalıyız. Bunların yerine üye fonksiyon versiyonlarını kullanmalıyız. İş bu üye fonksiyonlar ise 'std::vector' sınıfında mevcut değillerdir. >>>> 'std::vector' ve 'std::deque' sınıf şablonlarında olmayan ama algoritma fonksiyonlarının üye fonksiyon halleri olan üye fonksiyonların incelenmesi: >>>>> '.sort()' : 'sort()' algoritma fonksiyonunun üye fonksiyon hali. * Örnek 1, //.. int main() { /* # OUTPUT # tijen enes dogan cengiz ferhat aslihan ciler bilgin melih necmiye nazif ----------------------------------------------------------------------------- aslihan bilgin cengiz ciler dogan enes ferhat melih nazif necmiye tijen ----------------------------------------------------------------------------- tijen necmiye nazif melih ferhat enes dogan ciler cengiz bilgin aslihan ----------------------------------------------------------------------------- enes ciler dogan melih nazif tijen bilgin cengiz ferhat aslihan necmiye ----------------------------------------------------------------------------- */ std::list lString; fc(lString, 11, rname); print(lString); lString.sort(); print(lString); lString.sort(std::greater{}); print(lString); lString.sort( [](const std::string& s1, const std::string& s2){ return s1.size() < s2.size() || ( s1.size() == s2.size() && s1 < s2 ); } ); print(lString); } >>>>> '.reverse()' : 'reverse()' algoritma fonksiyonunun üye fonksiyon hali. >>>>> '.remove()' : 'remove()' algoritma fonksiyonunun üye fonksiyon hali. Üye fonksiyon olduğundan GERÇEK SİLME YAPAR. >>>>> '.merge()' : Sıralı birleştirme işlemidir. Kaynak olarak kullanılan kap boşalacaktır. Aynı sıralama kriteri kullanılmalıdır. * Örnek 1, //.. int main() { /* # OUTPUT # agah cihan kamil melek soner yasin alican cumhur furkan yalcin kurthan ----------------------------------------------------------------------------- ediz askin hulki nedim sevim yeliz dilber muhsin niyazi yilmaz emirhan ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- agah ediz askin cihan hulki kamil melek nedim sevim soner yasin yeliz alican cumhur dilber furkan muhsin niyazi yalcin yilmaz emirhan kurthan ----------------------------------------------------------------------------- */ auto f = [](const std::string& s1, const std::string& s2){ return s1.size() < s2.size() || ( s1.size() == s2.size() && s1 < s2 ); }; std::list lString; fc(lString, 11, rname); lString.sort(f); print(lString); std::list lStringTwo; fc(lStringTwo, 11, rname); lStringTwo.sort(f); print(lStringTwo); lStringTwo.merge(lString, f); print(lString); print(lStringTwo); } >>>>> '.splice()' : İstediğimiz kadar öğeyi bir zincirden kopartıp başka zincire eklemektedir. Sıralı olması gibi bir koşul söz konusu değil. * Örnek 1, //.. int main() { /* # OUTPUT # nurdan aslican hakan sevda kelami poyraz tugra ercument gazi celik dogan ----------------------------------------------------------------------------- julide askin cemile kunter pelin dogan nurdan hikmet fazilet nazife atif ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- nurdan aslican hakan sevda kelami poyraz tugra ercument gazi celik dogan julide askin cemile kunter pelin dogan nurdan hikmet fazilet nazife atif ----------------------------------------------------------------------------- */ // auto f = [](const std::string& s1, const std::string& s2){ // return s1.size() < s2.size() || ( s1.size() == s2.size() && s1 < s2 ); // }; std::list lString; fc(lString, 11, rname); print(lString); std::list lStringTwo; fc(lStringTwo, 11, rname); print(lStringTwo); lStringTwo.splice(lStringTwo.begin(), lString); print(lString); print(lStringTwo); } * Örnek 2, //.. int main() { /* # OUTPUT # nazli abdullah temel muruvvet ufuk zarife yurdanur hakan olcay poyraz pinat ----------------------------------------------------------------------------- tijen saadet pinat aynur onat nazli rupen berivan pelinsu necmiye melike ----------------------------------------------------------------------------- abdullah temel muruvvet ufuk zarife yurdanur hakan olcay poyraz pinat ----------------------------------------------------------------------------- tijen saadet pinat aynur onat nazli rupen berivan pelinsu necmiye nazli melike ----------------------------------------------------------------------------- */ // auto f = [](const std::string& s1, const std::string& s2){ // return s1.size() < s2.size() || ( s1.size() == s2.size() && s1 < s2 ); // }; std::list lString; fc(lString, 11, rname); print(lString); std::list lStringTwo; fc(lStringTwo, 11, rname); print(lStringTwo); lStringTwo.splice(prev(lStringTwo.end()), lString, lString.begin()); print(lString); print(lStringTwo); } >>> 'std::forward_list' sınıfının incelenmesi : Ender kullanım alanı vardır. Daha çok çifte bağlı liste kullanılır. C++11 ile dile eklendi. C dilindeki bağlı liste ile benzer performansı sağlaması amaçlanmıştır. Dolayısıyla '.size()' üye fonksiyonu mevcut değildir. Çifte bağlı listede her iki yöne de gidebiliyorken, tekli bağlı listede sadece tek yöne gidebiliyoruz. Bu da şöyle bir problemi beraberinde getirmektedir; çifte bağlı listede ilgili 'n' konumuna ekleme yapabiliyoruz fakat tekli bağlı listede ilgili 'n' konumuna ekleme yapabilmek için bizim 'n-1' konumu gerekiyor. Dolayısıyla tekli bağlı listedeki ekleme yapan üye fonksiyonların isimleri de 'insert_after' şeklindedir. Bu durumu şu şekilde açıklayabiliriz: 'n-1' konumundaki düğümün içerisindeki göstericiyi kullanarak 'n' konumundaki düğümün içerisindeki 'veri' teyit ediliyor. Eğer doğru ise ekleme yapılıyor. Benzer işlemler silme işlemleri için de geçerlidir. ÖZETLE EKLEME VE SİLME İŞLEMLERİ YAPAN FONKSİYONLARA GEÇİLEN KONUM BİLGİSİNDEN SONRAKİ KONUMA VEYA ÖNCEKİ KONUMA EKLEME/SİLME İŞLEMİ YAPILIYOR. O zaman aklımıza şu soru geliyor; ilk öğenin konumuna nasıl ekleme ve silme işlemi yapılacak? Bunun için de '.before_begin()' isimli bir fonksiyon mevcuttur. Kendisi bir iteratör döndürmekte olup, ilk düğümü gösteren 'anchor' düğümünün konumunu döndürmekte. ASLA VE ASLA 'derefence' ETMEMELİYİZ. Bu fonksiyonun geri döndürdüğü iteratörü ekleme ve silme işlemlerine argüman olarak geçeceğiz. Bu sıfın şablonunun iteratör grubu ise 'forward_iterator' grubundandır. Aşağıdaki örnekleri inceleyelim: * Örnek 1, //.. int main() { /* # OUTPUT # I: tansel nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- II: tansel AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- III: BBBBB tansel AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- IV: CCCCC BBBBB tansel AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- V: CCCCC tansel AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- VI: tansel AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- VII: AAAAA nasrullah binnur suleyman olcay ----------------------------------------------------------------------------- */ std::forward_list myList; for(int i = 0; i < 5; ++i) myList.push_front(rname()); print(myList); // I myList.insert_after(myList.begin(), "AAAAA"); // İmplementason gereği aldığı konumdan sonraki konuma ekleme yapmakta. print(myList); // II myList.insert_after(myList.before_begin(), "BBBBB"); // 'anchor' düğümünün konumunu geçtiğimiz için ilk öğenin konumuna eklendi. print(myList); // III myList.push_front("CCCCC"); // 'insert_after' ile benzer mottoda. Fakat 'insert_after' gereken yerler de vardır. print(myList); // IV myList.erase_after(myList.begin()); // Silme işleminden sonra silinen öğeden sonraki öğenin konumunu döndürmektedir. print(myList); // V myList.erase_after(myList.before_begin()); print(myList); // VI myList.pop_front(); print(myList); // VII return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # tarik ercument dogan mukerrem celal beril yurdagul zeliha tonguc abdulmuttalip ----------------------------------------------------------------------------- Silinecek isim : zeliha zeliha tonguc abdulmuttalip ----------------------------------------------------------------------------- */ std::forward_list myList; for(int i = 0; i < 10; ++i) myList.push_front(rname()); print(myList); std::string nameToDelete; std::cout << "Silinecek isim : "; std::cin >> nameToDelete; if( auto iter = std::find(myList.begin(), myList.end(), nameToDelete); iter != myList.end()) { myList.erase_after(myList.before_begin(), iter); } print(myList); return 0; } * Örnek 3.1, mülakat sorusu: //.. int main() { /* # OUTPUT # sidre burak binnur busra tonguc ceylan alev papatya aycan kayahan ----------------------------------------------------------------------------- Silinecek isim : busra silindi... sidre burak binnur tonguc ceylan alev papatya aycan kayahan ----------------------------------------------------------------------------- */ std::forward_list myList; for(int i = 0; i < 10; ++i) myList.push_front(rname()); print(myList); std::string nameToDelete; std::cout << "Silinecek isim : "; std::cin >> nameToDelete; auto iter_erase = myList.before_begin(); auto iter = myList.begin(); for(; iter != myList.end(); ++iter, ++iter_erase) if(*iter == nameToDelete) break; if(iter != myList.end()) { std::cout << "<" << nameToDelete << "> silindi...\n"; myList.erase_after(iter_erase); } print(myList); return 0; } * Örnek 3.2, //.. int main() { /* # OUTPUT # orkun nahit gul naciye recep sezen mert sadullah tarcan yalcin ----------------------------------------------------------------------------- Silinecek isim : sadullah silindi... orkun nahit gul naciye recep sezen mert tarcan yalcin ----------------------------------------------------------------------------- */ std::forward_list myList; for(int i = 0; i < 10; ++i) myList.push_front(rname()); print(myList); std::string nameToDelete; std::cout << "Silinecek isim : "; std::cin >> nameToDelete; for(auto iter = myList.before_begin(); iter != myList.end(); ++iter) { if( *next(iter) == nameToDelete ) { std::cout << "<" << nameToDelete << "> silindi...\n"; myList.erase_after(iter); break; } } print(myList); return 0; } >>> 'Associative Containers' : 'std::set', 'std::map', 'std::multiset' ve 'std::multimap' sınıf şablonlarıdır. İkili arama ağacı veri yapısını kullanır. Bir karşılaştırma ilişkisine göre öğeler kap içerisinde konumlanmakta. Dolayısla istediğimiz konuma değer ekleyemiyoruz. Bu dört sınıf şablonlarındaki üye fonksiyonlar neredeyse birbirinin aynısı. Arada küçük nüanslar bulunmaktadır. 'std::set' kabında bir anahtardan sadece bir tane olabilir. O değerden ikinci bir tanesi EKLENMEYECEKTİR. Fakat 'std::multiset' kabında o değerden birden fazla değer olabilir. İkili arama ağacında o türden bir veri tutulmakta. 'std::map' kabında ise anahtar-değer çiftleri tutulur. Her anahtarı tutan bir değer vardır yani. Yani ikili arama ağacında 'std::pair' sınıf şablonu tutulur. Yine bu sınıf kabında da sadece bir tane anahtar mevcut ama farklı anahtarlar aynı değeri tutabilirler. 'std::multimap' ise bünyesinde aynı anahtardan birden fazla çift tutabilir. Son olarak 'std::set' ve 'std::multiset' sınıfları 'set' isimli başlık dosyasında, 'std::map' ve 'std::multimap' sınıfları ise 'map' isimli başlık dosyasındadır. >>>> 'std::set' sınıf şablonu : Aşağıdaki temsili şekli inceleyelim: /* 78 28 195 15 31 152 213 */ Görüldüğü üzere bu ikili ağacı denge durumundadır. Bu durumdayken ekleme/silme/arama işlemleri logaritmik karmaşıklık düzeyindedir. İş bu sınıf şablonu varsayılan karşılaştırma ilişkini 'std::less' isimli fonksiyon nesnesi ile kurmaktadır. * Örnek 1, //.. int main() { std::set mySet; // std::set> mySet; } >>>>> Eğer karşılaştırma kritleri olarak 'std::less()' kullanılacaksa, sınıf türden elemanları saklayabilmek için ilgili sınıfımız '.operator<()' fonksiyonunu 'overload' etmelidir. Buradan hareketle diyebiliriz ki karşılaştırma kriterini neyle yapıyorsak sınıfımızda ilgili operatör de 'overload' edilmiş olmalı. Karşılaştırma 'equivalance' kurallarına göre yapılmaktadır. Fakat karşılaştırma operatörlerini bizler de yazabiliriz. Bir functor, 'lambda-expression' veya bir fonksiyon gelebilir; bir 'callable' geçebiliriz. Eğer bizler yazıyorsak yazdığımız kriter 'strict weak ordering' kurallarına tabii olması gerekiyor, 'Tanımsız Davranış' neden olmaması gerekiyor. * Örnek 1, //.. class Data{}; int main() { Data myData; std::set mySet; mySet.insert(myData); // Sentaks Hatası. std::set> mySetTwo; mySetTwo.insert(myData); // Sentaks Hatası. } * Örnek 2, //.. template using greaterSet = std::set>; int main() { greaterSet mySet; // Yazım kolaylığı sağlanmış oldu. } * Örnek 3, //.. class myComparator{ public: bool operator()(int x, int y)const{ return y < x; } // Daha karmaşık karşılaştırma kriteri de belirleyebiliriz. }; int main() { std::set mySet; } >>>>>> 'strict weak ordering' : 'operator' kelimesi, bizim karşılaştırma kriterimiz olsun. Yazmış olduğumuz karşılaştırma kriteri aşağıdaki garantileri vermek ZORUNDADIR. >>>>>>> 'antisymmetric' : "x operator y" işleminin sonucu 'true' ise "y operator x" işleminin sonucu 'false' OLMALI. >>>>>>> 'irreflexive' : "x operator x" işleminin sonucu her zaman 'false' OLMALI. >>>>>>> 'transitive' : "x operator y" işleminin sonucu 'true', "y operator z" işleminin sonucu da 'true' ise "x operator z" işleminin sonucu da 'true' OLMALI. >>>>>>> 'transitivity of equivalance' : "!(x operator y) && !(y operator x)" işleminin sonucu 'true', "!(y operator z) && !(z operator y)" işleminin sonucu da 'true' ise "!(x operator z) && !(z operator x)" işlemi de 'true' OLMALI. >>>>> 'std::set' sınıf şablonu bünyesindeki üye fonksiyonların incelenmesi: >>>>>> '.insert()' fonksiyonu: Argüman olarak aldığı değeri, kap içerisinde uygun bir yere ekler. * Örnek 1, //.. int main() { /* # OUTPUT # 1563 eklendi... 1589 eklendi... 1527 eklendi... 172 eklendi... 753 eklendi... 965 eklendi... 1373 eklendi... 1807 eklendi... 467 eklendi... 812 eklendi... 172 467 753 812 965 1373 1527 1563 1589 1807 */ std::set mySet; Irand rand{ 0, 2000 }; for(int i = 0; i < 10; ++i) { int ival = rand(); std::cout << ival << " eklendi...\n"; mySet.insert(ival); } for(auto iter = mySet.cbegin(); iter != mySet.cend(); ++iter) std::cout << *iter << " "; // Çıktıda da görüleceği üzere küçük büyüğe doğru ekleme yapıldı. return 0; } * Örnek 2.1, karşılaştırma kriteri olarak bir 'functor' kullanmak: //.. class myComparatorClass{ public: bool operator()(int a, int b)const { return a>b; } }; int main() { /* # OUTPUT # 9 7 6 5 3 2 1 */ std::set mySet; for(int i = 0; i < 10; ++i) { mySet.insert(rand() % 10); } for(auto iter = mySet.cbegin(); iter != mySet.cend(); ++iter) std::cout << *iter << " "; std::cout << "\n"; return 0; } * Örnek 2.2, karşılaştırma kriteri olarak bir fonksiyon kullanmak: //.. bool myComparatorFunc(int a, int b) { return a>b; } int main() { /* # OUTPUT # 19 17 16 15 13 12 11 */ std::set mySetTwo(myComparatorFunc); // std::set mySetTwo(myComparatorFunc); for(int i = 0; i < 10; ++i) { mySetTwo.insert(rand() % 10 + 10); } for(auto iter = mySetTwo.cbegin(); iter != mySetTwo.cend(); ++iter) std::cout << *iter << " "; std::cout << "\n"; return 0; } * Örnek 2.3, karşılaştırma kriteri olarak bir 'lambda-expression' kullanmak: //.. auto f = [](int a, int b){ return a>b; }; int main() { /* # OUTPUT # 29 27 26 25 23 22 21 */ std::set mySetThree; // C++20'ye kadar bu bildirim sentaks hatasıdır çünkü 'lambda-expression' ile yazılan // sınıfların 'Default Ctor.' fonksiyonları 'delete' edildi. // std::set mySetThree(f); // C++20 öncesi dönem. for(int i = 0; i < 10; ++i) { mySetThree.insert(rand() % 10 + 20); } for(auto iter = mySetThree.cbegin(); iter != mySetThree.cend(); ++iter) std::cout << *iter << " "; std::cout << "\n"; return 0; } >>>>>> 'Default Ctor.', 'Initializer-list Ctor.' ya da 'range' alan bir 'Ctor.' fonksiyonu vardır. Diğer kaplarda olduğu gibi 'size_t' alan ya da 'size_t' ve 'char' alan 'Ctor.' YOKTUR. Yine bu fonksiyonlara bir karşılaştırma kriter bilgisi de geçebiliriz. * Örnek 1, //.. int main() { /* # OUTPUT # 30 47 168 200 205 253 345 347 394 400 401 433 472 703 758 767 786 795 857 901 ----------------------------------------------------------------------------- 168 400 30 758 205 857 347 433 200 394 786 472 703 795 253 345 401 901 767 47 ----------------------------------------------------------------------------- */ std::set mySet; Irand myRand{ 0, 1000 }; while( mySet.size() < 20 ) mySet.insert(myRand()); print(mySet); std::mt19937 myRandomEngine; std::vector myVec{ mySet.begin(), mySet.end() }; std::shuffle( myVec.begin(), myVec.end(), myRandomEngine ); print(myVec); return 0; } >>>>>> Ekleme için '.insert()' ve '.emplace()' fonksiyonlarını kullanabiliriz. Burada '.insert()' fonksiyonunun geri dönüş değeri 'std::pair::iterator, bool>' şeklindedir. '.insert_hint()' fonksiyonu 'iterator' alan bir 'overload' a sahiptir. İş bu 'overload' versiyonu ise ilgili değerin olup olmadığının aranması işleminin geçilen bu konum bilgisinden itibaren gerçekleşmesini beyan etmektedir. Ek olarak 'std::initializer_list' ve 'range' parametreli versiyonları da mevcuttur iş bu '.insert()' için. '.insert()' fonksiyonunun özellikleri '.emplace()' fonksiyonu için de geçerli. Tek farkı 'perfect-forward' ile direkt olarak 'Ctor.' fonksiyonuna iletiyor. * Örnek 1, //.. int main() { // İş bu rakamlar küçükten büyüğe doğru kaba eklenecektir. std::set mySet{ 1, 4, 7, 9, 6, 3, 2, 5 }; std::pair::iterator, bool> x = mySet.insert(69); // auto x = mySet.insert(71); // Eğer '69' rakamı 'std::set' içerisinde halihazırda VARSA, '.insert()' geri dönüş değeri olan // 'std::pair<>' sınıfının; // i. 'second' isimli veri elemanı 'false' olacak. // ii. 'first' isimli veri elemanı ise o var olan halihazırdaki öğenin konum bilgisi olacak. // Eğer '69' rakamı 'std::set' içerisinde halihazırda YOKSA, '.insert()' geri dönüş değeri olan // 'std::pair<>' sınıfının; // i. 'second' isimli veri elemanı 'true' olacak. // ii. 'first' isimli veri elemanı ise o yeni eklenen öğenin konum bilgisi olacak. /* # OUTPUT # 69 degeri 8 konumuna eklenmistir. */ if(x.second) std::cout << *(x.first) << " degeri " << std::distance(mySet.begin(), x.first) << " konumuna eklenmistir.\n"; else std::cout << *(x.first) << " degeri " << std::distance(mySet.begin(), x.first) << " konumunda mevcuttur.\n"; /* # OUTPUT # 3 degeri 2 konumunda mevcuttur. */ x = mySet.insert(3); if(x.second) std::cout << *(x.first) << " degeri " << std::distance(mySet.begin(), x.first) << " konumuna eklenmistir.\n"; else std::cout << *(x.first) << " degeri " << std::distance(mySet.begin(), x.first) << " konumunda mevcuttur.\n"; return 0; } >>>>>> Arama için; eğer nihai amacımız sadece ve sadece var/yok sorgulaması yapmaksa, yani bulunduğunda o öğeye erişme amacımız yoksa, '.count()' fonksiyonu bu iş için verimli olabilir. 'std::multiset' için bu çağrı ilgili öğeden toplam kaç adet olduğunu bildirmektedir. Bunun haricinde '.find()' fonksiyonunu gerçekten de ilgili öğeye erişmek için kullanabiliriz. Geri döndürdüğü şey bir iteratör ve bulduğu nesnenin konumunu döndürmektedir. 'std::multiset' için iş bu fonksiyon ilk öğenin konumunu döndürmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # afyonkarahisar antalya artvin aydin batman cankiri diyarbakir elazig erzurum giresun karabuk kastamonu kirikkale kirsehir kocaeli osmaniye samsun siirt sivas tokat ----------------------------------------------------------------------------- siirt Evet var. */ std::set mySet; fcs(mySet, 20, rtown); print(mySet); std::string nameToCount; std::cin >> nameToCount; if(mySet.count(nameToCount)) std::cout << "Evet var.\n"; else std::cout << "Hayir, yok.\n"; return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # adana afyonkarahisar amasya ankara antalya ardahan bartin diyarbakir erzincan gaziantep kars konya mardin ordu osmaniye rize samsun sinop trabzon yozgat ----------------------------------------------------------------------------- yozgat Aranan yozgat isimli sehir 19. konumda bulundu. */ std::set mySet; fcs(mySet, 20, rtown); print(mySet); std::string nameToCount; std::cin >> nameToCount; if( auto iter = mySet.find(nameToCount); iter != mySet.end()) { std::cout << "Aranan " << *iter << " isimli sehir " << std::distance(mySet.begin(), iter) << ". konumda bulundu.\n"; } else { std::cout << "Aranan " << nameToCount << " isimli sehir BULUNAMADI.\n"; } return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # abdulmuttalip ali deniz ihsan turgut ----------------------------------------------------------------------------- ihsan => true ulya => false */ std::set maleNames{ "ali", "turgut", "ihsan", "deniz", "abdulmuttalip" }; print(maleNames); std::cout << "ihsan => " << ( maleNames.contains("ihsan") ? "true" : "false" ) << "\n"; std::cout << "ulya => " << ( maleNames.contains("ulya") ? "true" : "false" ) << "\n"; return 0; } >>>>>> Silme işlemleri için '.erase()' fonksiyonunu kullanabiliriz ki bu fonksiyon 'iterator' alan , 'range' alan vs. 'overload' lara sahip. 'std::multiset' için '.erase()' fonksiyonu silinen toplam adedi döndürmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # I: aksaray bartin canakkale igdir malatya sirnak ----------------------------------------------------------------------------- II: aksaray canakkale igdir malatya sirnak ----------------------------------------------------------------------------- III: aksaray canakkale malatya sirnak ----------------------------------------------------------------------------- Silinecel sehir : malatya [malatya] isimli sehir silindi... IV: aksaray canakkale sirnak ----------------------------------------------------------------------------- */ std::set mySet; fcs(mySet, 6, rtown); print(mySet); // I mySet.erase(std::next(mySet.begin())); print(mySet); // II mySet.erase( std::next(mySet.begin(), 2), std::prev(mySet.end(), 2)); print(mySet); // III std::string townToDelete; std::cout << "Silinecel sehir : "; std::cin >> townToDelete; if(auto iter = mySet.find(townToDelete); iter != mySet.end()) { mySet.erase(iter); std::cout << "[" << townToDelete << "] isimli sehirler silindi...\n"; } else { std::cout << "[" << townToDelete << "] isimli sehir silinemedi...\n"; } print(mySet); // IV return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # adiyaman amasya denizli hakkari karaman kayseri mus usak yalova yozgat ----------------------------------------------------------------------------- Sirasiyla eski ve yeni isimleri girin => yalova kirikkale Sirasiyla eski ve yeni isimleri girin => usak ankara adiyaman amasya ankara denizli hakkari karaman kayseri kirikkale mus yozgat ----------------------------------------------------------------------------- */ std::set mySet; fcs(mySet, 10, rtown); print(mySet); // I std::string old_name, new_name; std::cout << "Sirasiyla eski ve yeni isimleri girin => "; std::cin >> old_name >> new_name; // C++17 oncesi auto iter = mySet.find(old_name); if(iter != mySet.end()) { mySet.erase(iter); // Anahtar ağaçtan çıkartıldı ve içindeki nesne de yok edildi.. mySet.insert(new_name); // Yeni bir nesne hayata geldi ve ağaca eklendi. } std::cout << "Sirasiyla eski ve yeni isimleri girin => "; std::cin >> old_name >> new_name; // C++17 ve sonrası if(auto iter = mySet.find(old_name); iter != mySet.end()) { // std::set::node_type handler = mySet.extract(old_name); // Artık anahtarımız fiilen ağaçtan çıkartıldı fakat yok edilmedi. auto handler = mySet.extract(old_name); // Anahtarımızın değerini değiştirdik. handler.value() = new_name; // Yeni anahtarımızı ağaca ekledik fakat anahtarımızı'R-Value' haline getirmemiz zorunlu. mySet.insert(std::move(handler)); } print(mySet); return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # abdulmuttalip ali deniz ihsan turgut ----------------------------------------------------------------------------- ayse belgin emine naciye zarife ----------------------------------------------------------------------------- abdulmuttalip ali ihsan turgut ----------------------------------------------------------------------------- ayse belgin deniz emine naciye zarife ----------------------------------------------------------------------------- */ std::set maleNames{ "ali", "turgut", "ihsan", "deniz", "abdulmuttalip" }; std::set femaleNames{ "ayse", "belgin", "zarife", "emine", "naciye" }; print(maleNames); print(femaleNames); if(auto handler = maleNames.extract("deniz"); handler) { femaleNames.insert(std::move(handler)); } print(maleNames); print(femaleNames); return 0; } * Örnek 4, //.. int main() { /* # OUTPUT # abdulmuttalip ali deniz ihsan turgut ----------------------------------------------------------------------------- ayse belgin emine naciye zarife ----------------------------------------------------------------------------- abdulmuttalip ali ayse belgin deniz emine ihsan naciye turgut zarife ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ std::set maleNames{ "ali", "turgut", "ihsan", "deniz", "abdulmuttalip" }; std::set femaleNames{ "ayse", "belgin", "zarife", "emine", "naciye" }; print(maleNames); print(femaleNames); maleNames.merge(femaleNames); print(maleNames); print(femaleNames); return 0; } >>>>>> '.key_comp()' : Karşılaştırma kriterini döndüren bir fonksiyondur. * Örnek 1, //.. int main() { /* # OUTPUT # abdulmuttalip ali deniz ihsan turgut ----------------------------------------------------------------------------- key_comp : St4lessINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE result : 1 */ std::set maleNames{ "ali", "turgut", "ihsan", "deniz", "abdulmuttalip" }; print(maleNames); std::cout << "key_comp : " << typeid(maleNames.key_comp()).name() << "\n"; // Aynı karşılaştırma kriterini kullanarak iki ismi karşılaştırdık. std::cout << "result : " << maleNames.key_comp()("ahmet", "ulya") << "\n"; return 0; } >>>>> 'std::set' sınıf kabındaki bir öğeyi değiştirmeye çalışmak SENTAKS HATASINA neden olur çünkü iteratör konumundaki nesne 'const' kabul edilmektedir. Sentaks hatasını ortadan kaldıracak bir kod yazmamız durumunda 'Tanımsız Davranış' meydana gelecektir. Peki değiştirmemiz gerekiyor ise ne yapmalıyız? Var olanı silip, yeni değeri tekrardan bağlı listeye eklemeliyiz. >>>>> Tıpkı bağlı listede olduğu gibi ikili arama ağacındaki düğümler, hangi ağaca ait olduklarını bilmemektedirler. >>>> 'std::set' ve 'std::multiset' farklılığa için bir örnek: * Örnek 1, //.. int main() { /* # OUTPUT # 01 Ekim 1961 Pazar 02 Mayis 2006 Sali 03 Mart 1983 Persembe 04 Agustos 1992 Sali 05 Aralik 1990 Carsamba 06 Aralik 2003 Cumartesi 07 Nisan 1968 Pazar 08 Haziran 1963 Cumartesi 09 Temmuz 1967 Pazar 10 Aralik 1977 Cumartesi 11 Ocak 2014 Cumartesi 12 Subat 2017 Pazar 14 Aralik 2019 Cumartesi 15 Ekim 2016 Cumartesi 16 Mart 1955 Carsamba 17 Agustos 1992 Pazartesi 18 Agustos 1993 Carsamba 19 Nisan 2011 Sali 20 Subat 2018 Sali 21 Haziran 1997 Cumartesi 22 Ekim 1953 Persembe 23 Kasim 1997 Pazar 24 Eylul 2007 Pazartesi 25 Eylul 1992 Cuma 26 Subat 1990 Pazartesi 27 Aralik 1978 Carsamba 28 Agustos 1972 Pazartesi 29 Kasim 1978 Carsamba 30 Ekim 2007 Sali 31 Ekim 1960 Pazartesi ----------------------------------------------------------------------------- 01 Nisan 1998 Carsamba 01 Agustos 1966 Pazartesi 01 Haziran 1957 Cumartesi 02 Eylul 1991 Pazartesi 02 Temmuz 1984 Pazartesi 02 Nisan 1952 Carsamba 02 Eylul 2016 Cuma 02 Ekim 1961 Pazartesi 03 Eylul 1984 Pazartesi 03 Temmuz 1962 Sali 03 Kasim 2003 Pazartesi 03 Ocak 1967 Sali 03 Ocak 1951 Carsamba 04 Mart 2016 Cuma 04 Aralik 1995 Pazartesi 04 Ocak 1982 Pazartesi 05 Aralik 1955 Pazartesi 05 Kasim 1973 Pazartesi 05 Ekim 1992 Pazartesi 06 Subat 1974 Carsamba 07 Eylul 2000 Persembe 07 Subat 1976 Cumartesi 07 Kasim 1993 Pazar 07 Mayis 1978 Pazar 08 Agustos 1961 Sali 08 Haziran 1992 Pazartesi 08 Ekim 1967 Pazar 08 Agustos 1969 Cuma 09 Aralik 2005 Cuma 10 Haziran 1990 Pazar 10 Haziran 1986 Sali 10 Aralik 1980 Carsamba 11 Subat 2001 Pazar 11 Temmuz 1952 Cuma 11 Aralik 1994 Pazar 11 Agustos 1986 Pazartesi 12 Eylul 1983 Pazartesi 12 Ocak 1985 Cumartesi 12 Mayis 1974 Pazar 12 Subat 1986 Carsamba 13 Aralik 1989 Carsamba 13 Ocak 2006 Cuma 13 Haziran 1986 Cuma 13 Temmuz 1958 Pazar 13 Haziran 1971 Pazar 15 Eylul 2001 Cumartesi 15 Mart 1991 Cuma 15 Mayis 1995 Pazartesi 16 Aralik 1970 Carsamba 16 Nisan 1998 Persembe 16 Haziran 1950 Cuma 16 Aralik 1986 Sali 17 Subat 1959 Sali 17 Kasim 1950 Cuma 17 Ekim 2010 Pazar 17 Ekim 1975 Cuma 18 Temmuz 2000 Sali 19 Nisan 2006 Carsamba 19 Ocak 2010 Sali 19 Temmuz 2015 Pazar 19 Kasim 1980 Carsamba 19 Ekim 1965 Sali 19 Haziran 2017 Pazartesi 19 Haziran 1998 Cuma 20 Mayis 1957 Pazartesi 20 Subat 1986 Persembe 20 Mayis 1981 Carsamba 20 Aralik 1966 Sali 20 Temmuz 2002 Cumartesi 21 Eylul 1954 Sali 21 Ekim 2018 Pazar 21 Subat 1961 Sali 22 Ekim 2019 Sali 22 Temmuz 2010 Persembe 22 Subat 2019 Cuma 22 Mart 2006 Carsamba 23 Eylul 1985 Pazartesi 23 Mart 1996 Cumartesi 23 Mart 1974 Cumartesi 23 Temmuz 2006 Pazar 24 Aralik 1959 Persembe 24 Eylul 2016 Cumartesi 25 Kasim 2017 Cumartesi 25 Mayis 1990 Cuma 25 Kasim 2002 Pazartesi 26 Kasim 1960 Cumartesi 26 Haziran 2002 Carsamba 26 Kasim 1951 Pazartesi 26 Kasim 1962 Pazartesi 26 Temmuz 1986 Cumartesi 26 Mayis 1992 Sali 26 Haziran 1963 Carsamba 27 Ocak 1984 Cuma 27 Nisan 2010 Sali 29 Mayis 1989 Pazartesi 29 Eylul 1971 Carsamba 29 Ekim 2013 Sali 29 Subat 2004 Pazar 29 Ocak 1957 Sali 30 Aralik 1962 Pazar ----------------------------------------------------------------------------- */ auto f = [](const Date& dx, const Date& dy){ return dx.month_day() < dy.month_day(); }; std::set mySet; for(int i = 0; i < 100; ++i) mySet.insert(Date::random()); print(mySet, "\n"); std::multiset myMultiSet; for(int i = 0; i < 100; ++i) myMultiSet.insert(Date::random()); print(myMultiSet, "\n"); // Çıktıda da görüldüğü üzere 'std::multiset' aynı değere sahip birden fazla öğe içermektedir. // BURADAKİ ANAHTAR AYIN GÜNÜ. return 0; } >>>>> 'std::multiset' sınıf şablonundaki '.insert()' üye fonksiyonunun geri dönüş değeri doğrudan bir 'iterator' şeklindedir. >>>> 'std::map' sınıf şablonunun incelenmesi: Logaritmik karmaşıklıkta "Anahtar-Değer" çiftlerinin tutulmasıdır. Yine ikili arama ağacı veri yapısı kullanılır. Yine karşılaştırma usülüne göre kapta doldurma yapar. Tıpkı 'std::set' sınıf şablonunda olduğu gibi ilgili karşılaşırma kriterini biz belirleyebiliriz fakat varsayılan olarak anahtara bakarak küçükten büyüğe doğru yapmaktadır. 'std::multimap' den farkı tıpkı 'std::set' de olduğu gibi 'std::map' sadece bir adet 'Key' tutmakta fakat 'std::multimap' birden fazla 'Key' tutabilir. Aynı değerden birden fazla da olabilir. Artık kapta tutulan öğelerimiz birer 'std::pair<>' şeklinde. * Örnek 1, //.. int main() { /* # OUTPUT # */ std::map myMap; // Anahtara karşılık gelen tür 'int'. Bu durumda varsayılan karşılaştırma kriter 'std::less' // olacaktır. Değere karşılık gelen tür 'std::string' return 0; } >>>>> İlgili sınıfın özel üye fonksiyonlarının incelenmesi: >>>>>> 'Ctor.' fonksiyonları artık 'std::pair' almaktadır. Onun haricinde 'std::set' ile aynıdır. * Örnek 1, //.. int main() { /* # OUTPUT # <6,sinem> <9,hakan> <12,ayse> <15,korhan> */ std::map myMap { {12, "ayse"}, {9, "hakan"}, {6, "sinem"}, {15, "korhan"} }; // Anahtara karşılık gelen tür 'int'. Bu durumda varsayılan karşılaştırma kriter // 'std::less' olacaktır. Değere karşılık gelen tür 'std::string' for(const auto& index : myMap) std::cout << "<" << index.first << "," << index.second << ">\n"; return 0; } >>>>>> Ekleme işlemleri: '.insert()' fonksiyonuna bir 'std::pair<>' geçebiliriz. 'CTAD' gelmesi ile birlikte kullanım kolaylığı gelmiştir. 'std::set' ile aynı fonksiyonlara sahiptir. * Örnek 1, //.. int main() { /* # OUTPUT # <6,sinem> <9,hakan> <12,ayse> <15,korhan> --------------------- <3,nergis> <6,sinem> <9,hakan> <12,ayse> <15,korhan> <18,hulusi> --------------------- <3,nergis> <6,sinem> <9,hakan> <12,ayse> <15,korhan> <18,hulusi> <21,murda> */ std::map myMap { {12, "ayse"}, {9, "hakan"}, {6, "sinem"}, {15, "korhan"} }; // Anahtara karşılık gelen tür 'int'. Bu durumda varsayılan karşılaştırma kriter // 'std::less' olacaktır. Değere karşılık gelen tür 'std::string' for(const auto& index : myMap) std::cout << "<" << index.first << "," << index.second << ">\n"; std::cout << "---------------------\n"; myMap.insert( std::pair{18, "hulusi"} ); // C++17 öncesi myMap.insert({ 3, "nergis" }); // C++17 ve sonrası for(const auto& index : myMap) std::cout << "<" << index.first << "," << index.second << ">\n"; std::cout << "---------------------\n"; myMap.insert( std::make_pair(21, "murda") ); for(const auto& index : myMap) std::cout << "<" << index.first << "," << index.second << ">\n"; return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # cemil => 30 Mart 2010 Sali eylul => 05 Subat 2013 Sali gulden => 07 Mart 1990 Carsamba halime => 09 Mayis 1997 Cuma malik => 02 Aralik 1973 Pazar mukerrem => 10 Subat 1967 Cuma petek => 14 Ocak 1963 Pazartesi tayfun => 10 Kasim 1959 Sali tonguc => 12 Kasim 1997 Carsamba */ std::map myMap; for(int i = 0; i < 10; ++i) { myMap.insert( {rname(), Date::random()} ); } std::cout << std::left; for(const auto& [name, birth_date] : myMap) { std::cout << std::setw(12) << name << " => " << birth_date << "\n"; } return 0; } >>>>>> Arama işlemleri de yine 'std::set' ile aynıdır. * Örnek 1, //.. int main() { /* # OUTPUT # Aranacak ismi girin : ahmet ahmet, 70298129 ahmet, 172669440 ahmet, 377108003 ahmet, 385158732 ahmet, 450395738 ahmet, 572561811 ahmet, 575705360 ahmet, 603123090 ahmet, 628257755 ahmet, 660420424 ahmet, 796664164 ahmet, 838139053 ahmet, 869921280 ahmet, 886578186 ahmet, 899334107 ahmet, 919191847 ahmet, 1030273655 ahmet, 1104028380 ahmet, 1108199257 ahmet, 1311572935 ahmet, 1347584264 ahmet, 1362757102 ahmet, 1449189833 ahmet, 1506997827 ahmet, 1530548506 ahmet, 1632381616 ahmet, 1667250732 ahmet, 1685622011 ahmet, 1849557795 ahmet, 1943003493 ahmet, 2139842053 */ std::multimap myMap; fcs(myMap, 10'000, []{ return std::make_pair(rname(), rand()); }); std::cout << "Aranacak ismi girin : "; std::string nameToLookUp; std::cin >> nameToLookUp; auto [iterLowerBound, iterUpperBound] = myMap.equal_range(nameToLookUp); while( iterLowerBound != iterUpperBound ) { std::cout << iterLowerBound->first << ", " << iterLowerBound->second << "\n"; ++iterLowerBound; } return 0; } >>>>>> '.operator[]' fonksiyonu: Aşağıdaki örneği inceleyelim: * Örnek 1, //.. int main() { /* # OUTPUT # [bilgin, 58] [ediz, 48] [hilal, 51] [naci, 62] [nuri, 60] ----------------------------------------------------------------------------- İsim ve yaş bilgilerini girin: ahmet 31 [ahmet, 31] [bilgin, 58] [ediz, 48] [hilal, 51] [naci, 62] [nuri, 60] ----------------------------------------------------------------------------- */ /* # OUTPUT # [caner, 51] [nurullah, 60] [pelinsu, 62] [ugur, 48] [zekai, 58] ----------------------------------------------------------------------------- İsim ve yaş bilgilerini girin: caner 15 [caner, 15] [nurullah, 60] [pelinsu, 62] [ugur, 48] [zekai, 58] ----------------------------------------------------------------------------- */ map myMap; fcs(myMap, 5, []{ return std::make_pair(rname(), rand() % 60 + 5 ); }); print(myMap, "\n"); string name; int age; std::cout << "İsim ve yaş bilgilerini girin: "; std::cin >> name >> age; myMap[name] = age; std::cout << "\n\n"; print(myMap, "\n"); // Çıktıda görüldüğü üzere eğer 'Key' mevcut değil ise ikili arama ağacına ekleniyor; eğer mevcut // ise o 'Key' e karşılık gelen 'Value' değiştiriliyor. // Bu durumda şunu diyebiliriz ki; // i. İlgili anahtar bulunması durumunda, '.operator[]' fonksiyonunun geri dönüş değeri ilgili // 'Key-Value' çiftinin 'second' isimli veri elemanına referanstır. // ii. İlgili anahtar yok ise yeni bir 'Key-Value' çifti oluşturuluyor, ki 'first' ve 'second' isimli // veri elemanları değerlerini sırasıyla 'name' ve 'age' isimli değişkenlerden almaktadır, // ve '.operator[]' fonksiyonunun geri dönüş değeri yeni oluşturulan 'Key-Value' çiftinin 'second' // isimli veri elemanına referanstır. Fakat 'second' isimli veri elemanı ilk olarak 'Value Init.' // edilmektedir. BURADA GÖZ ARDI EDİLMEMESİ GEREKEN NOKTA İLGİLİ 'Key' YOK İSE YENİ BİR EKLEME // YAPILACAKTIR. } * Örnek 2, bir konteynırda bulunan isimlerden kaçar tane olduğunun hesaplanması: //.. int main() { /* # OUTPUT # [abdulmuttalip, 2] [alev, 1] [ali, 1] [alparslan, 1] [arda, 1] [askin, 1] [aslican, 1] [ata, 1] [atakan, 1] [atalay, 1] [atif, 1] [aynur, 1] [ayse, 1] [baran, 1] [beril, 1] [bilgin, 1] [binnur, 1] [burhan, 1] [candan, 1] [cebrail, 1] [ceyhan, 1] [ceyhun, 1] [cihat, 4] [devrim, 1] [diana, 1] [dogan, 1] [dost, 1] [ece, 1] [eda, 2] [edip, 1] [emine, 1] [engin, 1] [esen, 1] [esra, 1] [fadime, 2] [fahri, 1] [ferhat, 1] [fuat, 1] [gizem, 1] [gursel, 2] [hakan, 1] [haldun, 2] [haluk, 1] [hulki, 1] [hulya, 1] [iffet, 1] [izzet, 1] [kaan, 1] [kasim, 1] [kazim, 1] [kezban, 1] [korhan, 1] [mahir, 2] [melih, 1] [melike, 1] [metin, 1] [murathan, 1] [nagehan, 1] [nalan, 2] [nihat, 1] [nuri, 1] [nuriye, 1] [olcay, 3] [onat, 1] [papatya, 1] [petek, 2] [sabriye, 1] [sadegul, 1] [sami, 1] [saniye, 1] [selenay, 1] [sevda, 2] [sezen, 1] [su, 1] [taner, 1] [tansu, 1] [tarcan, 1] [tugra, 1] [turgut, 1] [ufuk, 1] [yasemin, 1] [yasin, 1] [yelda, 1] [zahide, 1] [zeliha, 1] [zerrin, 1] ----------------------------------------------------------------------------- */ std::vector sVec; fcs(sVec, 100, rname); std::map cMap; for(const auto& name: sVec) ++cMap[name]; // i. 'for' döngüsünün ilk turunda 'name' için 'Enes' isminin geldiğini varsayalım; // ii. Bu durumda kap boş olduğundan bir adet 'std::pair<>' oluşturulacak. Bunun 'first' isimli veri // elemanı 'Enes' ismini alırken, 'second' isimli veri elemanı ise '0' değeri ile hayata gelecek // çünkü 'Value Init.' edildi. Sonrasında da bu ifade '.operator++()' fonksiyonuna operand olacak ki // 'second' isimli veri elemanının değeri bir artacak. // iii. Döngünün ikinci turunda tekrardan 'Enes' ismi gelirse bizim 'second' bir artacak. Başka bir // isim gelirse yukarudaki işlemler gerçekleşecek. print(cMap, "\n"); } * Örnek 3, yukarıdaki örnekteki isimleri, 'Değer' bilgisine göre, Büyükten Küçüpe doğru sıralayalım: //.. int main() { /* # OUTPUT # [feramuz, 4] [nazif, 3] [ciler, 3] [rupen, 2] [murathan, 2] [kaya, 2] [kaan, 2] [nefes, 2] [nuriye, 2] [fugen, 2] [fahri, 2] [erdem, 2] [sami, 2] [soner, 2] [sumeyye, 2] [beyhan, 2] [tugra, 2] [anil, 2] [zarife, 1] [nuri, 1] [niyazi, 1] [nisan, 1] [nevsin, 1] [yurdagul, 1] [yurdanur, 1] [yelda, 1] [naz, 1] [nasrullah, 1] [mustafa, 1] [muslum, 1] [zubeyde, 1] [yilmaz, 1] [turhan, 1] [orkun, 1] [tunc, 1] [pakize, 1] [recep, 1] [kayahan, 1] [sadegul, 1] [sadullah, 1] [sarp, 1] [teoman, 1] [sidre, 1] [tarcan, 1] [binnur, 1] [durriye, 1] [dilek, 1] [ceyhun, 1] [ceyhan, 1] [cemile, 1] [cemal, 1] [can, 1] [bulent, 1] [ege, 1] [bilge, 1] [baran, 1] [azize, 1] [aslican, 1] [askin, 1] [asim, 1] [adem, 1] [hasan, 1] [melisa, 1] [melek, 1] [lale, 1] [abdullah, 1] [jade, 1] [irmak, 1] [huseyin, 1] [hulki, 1] [murat, 1] [gulsen, 1] [furkan, 1] [eylul, 1] [emrecan, 1] [emre, 1] [emine, 1] [egemen, 1] ----------------------------------------------------------------------------- */ std::vector sVec; fcs(sVec, 100, rname); std::map cMap; for(const auto& name: sVec) ++cMap[name]; std::vector> myVec{ cMap.begin(), cMap.end() }; // std::vector myVec{ cMap.begin(), cMap.end() }; // CTAD : Sınıf şablonlarında tür çıkarımı. std::sort( myVec.begin(), myVec.end(), [](const std::pair& p1, const std::pair& p2){ return p1.second > p2.second; } ); // Generalized Lambda Expression // std::sort( // myVec.begin(), // myVec.end(), // [](const auto& p1, const auto& p2){ // return p1.second > p2.second; // } // ); print(myVec, "\n"); } >>>>>> '.at()' fonksiyonu: C++11 ile dile eklenmiştir. Aranan 'Key' değeri ikili arama ağacında var ise değerini değiştiriyor. Fakat yok ise 'exception' fırlatmaktadır. * Örnek 1, //.. int main() { /* # OUTPUT # [cahit, 62] [hilal, 60] [sumeyye, 58] [tayyar, 48] [tugra, 51] ----------------------------------------------------------------------------- İsim ve yaş bilgilerini girin: ahmet 21 Hata yakalandi => map::at [cahit, 62] [hilal, 60] [sumeyye, 58] [tayyar, 48] [tugra, 51] ----------------------------------------------------------------------------- */ /* # OUTPUT # [caner, 51] [nurullah, 60] [pelinsu, 62] [ugur, 48] [zekai, 58] ----------------------------------------------------------------------------- İsim ve yaş bilgilerini girin: caner 15 [caner, 15] [nurullah, 60] [pelinsu, 62] [ugur, 48] [zekai, 58] ----------------------------------------------------------------------------- */ map myMap; fcs(myMap, 5, []{ return std::make_pair(rname(), rand() % 60 + 5 ); }); print(myMap, "\n"); string name; int age; try { std::cout << "İsim ve yaş bilgilerini girin: "; std::cin >> name >> age; myMap.at(name) = age; } catch(const std::exception& ex) { std::cout << "Hata yakalandi => " << ex.what() << "\n"; } std::cout << "\n\n"; print(myMap, "\n"); } >>>>>> '.try_emplace()' fonksiyonu : '.emplace()' fonksiyonu, kendisine geçilen argümanlar ile bir 'std::pair<>' oluşturmakta sonrasında bunu ikili arama ağacına eklemeye çalışmaktadır. Başarılı da olabilir başarısız da. '.try_emplace()' fonksiyonu ise önce ilgili ağaçta 'Key' var mı yok mu diye bakınıyor. Var ise 'std::pair<>' OLUŞTURMUYOR. >>> 'Unordered Associative Containers' : Diğer programlama dillerinde 'hash-set', 'hash-map' diye isimlendirilen veri yapılarının C++ dilindeki karşılığıdır. 'Hash-table' veri yapılarıdır. >>>> 'Hash-table' veri yapısı: Bir diziye elemanlar yerleştirsek ve bu dizide bir arama yapsak bu 'O(n)' karmaşıklığında olacaktı. Sıralı bir dizide 'binary_search' yaparsak da 'O(log(n))' karmaşıklığında olacaktır. İşte 'hash-table' ise bu ikisi arasında bir karmaşıklık sunmaktadır. Burada aramaya konu olan değer bir indise dönüştürülür, ki buna 'hashing' denir, ve ilgili indis kapta aranır. Bu 'hashing' işlemini yerine getiren fonksiyonlara da 'hash function' denmektedir. Bu fonksiyon ne kadar iyiyse farklı değerleri farklı indislere dönüştürmektedir. Velevki farklı değerleri aynı indise dönüştürürse, bu duruma da 'Collision' denmektedir. Fakat unutulmamalıdır ki ilgili 'hash-function' ne kadar iyi olursa olsun 'collision' kaçınılmazdır. Peki bizler bu 'collision' ihtimalini nasıl minimize edebiliriz? El-cevap: Veri Yapıları ve Algoritmalar biliminin konusu. * Örnek 1, /* Aramaya Konu Olan Değer => HashFunction => İndis Ahmet =================> 4 Merve =================> 2 Aylin =================> 6 Ulya =================> 4 // 'Collision' gerçekleşti. */ >>>>> Arka planda ise elemanları bağlı liste olan bir vektör bulunmaktadır. İlgili 'hash-function' sonucu elde edilen indis vektörde aranmakta ve erişilmekte. Eğer 'collision' olmuş ise bu sefer de o indisteki bağlı listede birden fazla düğümler meydana gelmekte. Bu da bizim tekrardan o düğümlerde arama yapmamızı gerektirmekte. * Örnek 1, temsili 'unordered-set' /* Vektör Bağlı Liste [0] => [AAA] Girdi => HashFunction [1] => [BBB] -> [bbb] [2] => [CCC] -> [ccc] -> [CcC] */ * Örnek 2, temsili 'unordered-map' /* Vektör Bağlı Liste [0] => [AAA - 111] Girdi => HashFunction [1] => [BBB - 222] -> [bbb - 333] [2] => [CCC - 444] -> [ccc - 555] -> [CcC - 666] */ >>>>> Standart kütüphanedeki 'primitive' türler ve standart kütüphanedeki sınıf şablonları için standart bir 'hash-function' mevcuttur. Kendi sınıf türlerimizi eğer bu veri yapısında tutmak istiyorsak, ilgili 'hash-function' ları bizler yazmalıyız. >>>> 'unoredered_set' ve 'unoredered_map': >>>>> İş bu sınıf şablonları sırasıyla 'std::set' ve 'std::map' sınıf şablonlarına çok benzemektedir. Fakat arka plandaki veri yapısının BİRBİRİNDEN FARKLI OLDUĞUNU UNUTMAYALIM. BİRİSİNDE 'BINARY TREE' KULLANILIRKEN, DİĞERİNDE 'HASH TABLE' KULLANILMAKTADIR. >>>>> Yine 'hash table' veri yapısındaki karşılaştırma kriteri '==' operatörü ile yapılırken, 'binary tree' veri yapısında '<' operatörü kullanılmakta. Buradan hareketle diyebiliriz ki iş bu kapta tutacağımız sınıf türleri için '.operator<()' fonksiyonunu yazmak lüzumsuz veya yeterli bilgiye sahip değilsek veya lojik açıdan da mümkün değilse ama '.operator==()' fonksiyonunu yazmak mantıklı ise 'Binary Tree' yerine 'Hash Table' kullanmalıyız. Buradan da diyebiliriz ki 'std::set' / 'std::map' sınıf şablonlarında karşılaştırma 'equivalence' ile fakat 'std::unordered_set' / 'std::unordered_map' sınıf şablonlarında ise 'equity' ile yapılmakta. >>>>> Geçmişte yazılan kodlar ile çakışmaması adına bu garip isim seçildi. Aslında bu kaplar birer 'hash_set' ve 'hash_map' kaplarıdır. Fakat bildirimleri sırasıyla 'unordered_set' ve 'unordered_map' başlık dosyasındadır. >>>>> İlgili sınıf şablonunun ikinci parametresi olan 'hasher' incelenmesi: * Örnek 1, standart kütüphanedeki 'std::hash' ve bizim yazdığımız 'hash' sınıfları: //.. template struct myHash { size_t operator()(const T& other){ return other.size() * 2 / 4; } }; int main() { /* # OUTPUT # [ahmet] => 6192890985721302765 [ahmet] => 2 */ std::string keyToHash = "ahmet"; std::cout << "[" << keyToHash << "] => " << std::hash{}(keyToHash) << "\n"; std::cout << "[" << keyToHash << "] => " << myHash{}(keyToHash) << "\n"; return 0; // Çıktıda görülen bu değerler, ilgili sınıf şablonu tarafından tekrardan indis bilgisine // dönüştürülecektir. } * Örnek 2, standart kütüphanedeki 'std::hash' fonksiyonunun bizim sınıfımız için özelleştirilmesi: //.. // İlgili 'hash' fonksiyonumuz 'std' isim alanında olduğundan, yazacağımız özelleştirilmiş şablon da // o isim alanında olmalı. Fakat bu tip 'std' için şablon türetmek haricinde 'std' için şeyler yazmak // 'Tanımsız Davranış' olur. namespace std { template<> // 'explicit specialization' struct hash{ size_t operator()(const Date& date) { /* Boost kütüphanesinin 'hash' fonksiyonu bu tip 'custom' türler için ideal. */ // Alternatif - I return std::hash{}(date.month_day()) + std::hash{}(date.month()) + std::hash{}(date.year()); } }; } int main() { /* # OUTPUT # [17 Eylul 1993 Cuma] => 2019 */ Date keyToHash(17, 9, 1993); std::cout << "[" << keyToHash << "] => " << std::hash{}(keyToHash) << "\n"; // 'std::hash' fonksiyonu için 'Date' açılımı standart kütüphanede olmadığından bizler // 'explicit specialization' yazmalıyız. // BİZLER BURADA STANDART OLAN 'std::hash' FONKSİYONUNU KULLANMAK İSTEDİĞİMİZ İÇİN ÖZELLEŞTİRME // YAPTIK. BU FONKSİYON YERİNE KENDİMİZ BİR 'functor' DA OLUŞTURABİLİRDİK. std::unordered_set mySet; // Yukarıdaki özelleştirme yapıldığı için artık burası SENTAKS HATASI DEĞİL. return 0; } * Örnek 3, kendi sınıfımız için 'custom-hasher' kullanılması. //.. struct DateHasher{ size_t operator()(const Date& date) { /* Boost kütüphanesinin 'hash' fonksiyonu bu tip 'custom' türler için ideal. */ // Alternatif - I return std::hash{}(date.month_day()) + std::hash{}(date.month()) + std::hash{}(date.year()); } }; int main() { // Yukarıdaki 'functor' yazıldığı için artık burası SENTAKS HATASI DEĞİL. std::unordered_set mySet; return 0; } * Örnek 4, kendi sınıfımız için 'custom-hasher' kullanılması. //.. auto dateHasher = [](const Date& date){ return std::hash{}(date.month_day()) + std::hash{}(date.month()) + std::hash{}(date.year()); }; int main() { std::unordered_set mySet; // Yukarıdaki 'lambda-expression' yazıldığı için artık burası SENTAKS HATASI DEĞİL. Ayrıca bu // kodun C++20 ile derlenmesi gerekiyor çünkü 'lambda-expressions' için 'default ctor.' 'delete' // EDİLMİŞ DEĞİL. std::unordered_set mySetTwo(100, dateHasher); // C++20 öncesi return 0; } >>>>> İlgili sınıf şablonunun üçüncü parametresi olan karşılaştırma kriterinin incelenmesi: * Örnek 1, //.. class myClass{}; namespace std{ template<> // 'explicit specialization' struct hash{ size_t operator()(const myClass& other){ return 31; } }; } int main() { /* # OUTPUT # error: no match for ‘operator==’ (operand types are ‘const myClass’ and ‘const myClass’) */ std::unordered_set> mySet; mySet.insert(myClass{}); // Sentaks hatası çünkü üçüncü şablon parametresi varsayılan argüman olarak 'std::equal_to' // kullanmakta fakat ilgili 'functor' bizim sınıfımız için tanımlı DEĞİL. return 0; } * Örnek 2, //.. class myClass{}; namespace std{ template<> // 'explicit specialization' struct hash{ size_t operator()(const myClass& other) const { return 31; } }; } const bool operator==(const myClass& m1, const myClass& m2) { return m1==m2; } int main() { /* # OUTPUT # */ std::unordered_set > mySet; mySet.insert(myClass{}); return 0; } * Örnek 3, //.. // Custom Hasher struct DateHasher{ size_t operator()(const Date& date)const{ return std::hash{}(date.month_day()) + std::hash{}(date.month()) + std::hash{}(date.year()); } }; // Custom Comparator struct DateEqual{ bool operator()(const Date& dateOne, const Date& dateTwo)const{ return dateOne == dateTwo; } // İlgili 'Date' sınıfı '.operator==()' fonksiyonunu 'overload' etmiştir. }; int main() { /* # OUTPUT # */ Date myDate(17, 9, 1993); std::unordered_set mySet; mySet.insert(myDate); return 0; } * Örnek 4, //.. // Custom class class myClass{}; // Custom Hasher struct DateHasher{ size_t operator()(const myClass& date)const{ return 31; } }; // Custom Comparator struct DateEqual{ // İlgili 'myClass' sınıfı '.operator==()' fonksiyonunu 'overload' etmediğinden, // bu şekilde yazıldı. bool operator()(const myClass& dateOne, const myClass& dateTwo)const{ return true; } }; int main() { /* # OUTPUT # */ myClass myDate; std::unordered_set mySet; mySet.insert(myDate); return 0; } >>>>> Unutulmamalıdır ki bu veri yapısında herhangi bir sıralama da söz konusu değildir. Yani öğeler eklenirken bir sıra gözetilmezler. >>>>> 'Hash-table' bünyesinde bulunan ilgili vektörün her bir indise aslında 'bucket' denmektedir. Dolayısıyla bu 'bucket' adedi performansı etkileyen en önemli etkenlerden birisidir. >>>>> 'std::set' / 'std::map' sınıf şablonlarında olmayan fonksiyonlar: >>>>>> '.hash_function()' : İlgili fonksiyonu döndüren üye fonksiyondur. * Örnek 1, //.. int main() { /* # OUTPUT # ahmet emine handan galip ercument cebrail garo alparslan berivan zubeyde ----------------------------------------------------------------------------- The Hasher : St4hashINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE */ std::unordered_set mySet; fcs(mySet, 10, rname); print(mySet); auto hashFunc = mySet.hash_function(); std::cout << "The Hasher : " << typeid(hashFunc).name() << "\n"; return 0; } >>>>>> '.bucket_count()' : İlgili kaptaki 'bucket' sayısını döndürmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # Size : 46 Bucket Size : 103 */ std::unordered_set mySet(100); fcs(mySet, 50, rname); std::cout << "Size : " << mySet.size() << "\n"; std::cout << "Bucket Size : " << mySet.bucket_count() << "\n"; return 0; } >>>>>> '.load_factor()' : Kapta tutulan öğe sayısının 'bucket' sayısına oranını döndürmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # Size : 47 Bucket Size : 103 Load Factor : 0.456311 Load Factor : 0.456311 */ std::unordered_set mySet(100); fcs(mySet, 50, rname); std::cout << "Size : " << mySet.size() << "\n"; std::cout << "Bucket Size : " << mySet.bucket_count() << "\n"; std::cout << "Load Factor : " << mySet.load_factor() << "\n"; std::cout << "Load Factor : " << static_cast(mySet.size()) / mySet.bucket_count() << "\n"; return 0; } >>>>>> '.max_load_factor' : Öyle bir 'load_factor' oranı ki o orana geldiğinde 'rehash' yapılacak. Yani yeni 'bucket' eklenecek ve 'hash table' yeniden düzenlenecek. Bu oranı kendimiz de belirleyebiliyoruz. * Örnek 1, //.. int main() { /* # OUTPUT # Size : 47 Bucket Size : 103 Load Factor : 0.456311 Load Factor : 0.456311 Max Load Factor : 1 Max Load Factor : 2.31 */ std::unordered_set mySet(100); fcs(mySet, 50, rname); std::cout << "Size : " << mySet.size() << "\n"; std::cout << "Bucket Size : " << mySet.bucket_count() << "\n"; std::cout << "Load Factor : " << mySet.load_factor() << "\n"; std::cout << "Load Factor : " << static_cast(mySet.size()) / mySet.bucket_count() << "\n"; std::cout << "Max Load Factor : " << mySet.max_load_factor() << "\n"; mySet.max_load_factor(2.31); std::cout << "Max Load Factor : " << mySet.max_load_factor() << "\n"; return 0; } >>>>>> '.bucket_size()' : İlgili 'bucket' içerisindeki eleman sayısını döndürmektedir. >>>>>> '.rehash()' : 'bucket' sayısını arttırmak için kullanılır. Tekrardan 'rehash' işlemi yapmaktadır. >>>>>> Pekiştirici örnek, //.. int main() { /* # OUTPUT # Size : 41 Bucket Size : 103 Load Factor : 0.398058 Load Factor : 0.398058 Max Load Factor : 1 [ 0], (0) => [ 1], (0) => [ 2], (0) => [ 3], (1) => yeliz [ 4], (0) => [ 5], (2) => pinat fahri [ 6], (1) => muruvvet [ 7], (1) => hakki [ 8], (0) => [ 9], (0) => [ 10], (2) => azize muslum [ 11], (1) => tarik [ 12], (0) => [ 13], (0) => [ 14], (2) => petek burak [ 15], (0) => [ 16], (1) => bulent [ 17], (1) => cemile [ 18], (0) => [ 19], (0) => [ 20], (1) => orkun [ 21], (1) => sadiye [ 22], (0) => [ 23], (0) => [ 24], (1) => izzet [ 25], (1) => emirhan [ 26], (0) => [ 27], (0) => [ 28], (0) => [ 29], (0) => [ 30], (0) => [ 31], (0) => [ 32], (0) => [ 33], (1) => nuriye [ 34], (0) => [ 35], (0) => [ 36], (0) => [ 37], (1) => kasim [ 38], (0) => [ 39], (0) => [ 40], (2) => tarkan hulusi [ 41], (0) => [ 42], (0) => [ 43], (0) => [ 44], (2) => lale leyla [ 45], (0) => [ 46], (0) => [ 47], (0) => [ 48], (0) => [ 49], (0) => [ 50], (0) => [ 51], (0) => [ 52], (0) => [ 53], (0) => [ 54], (0) => [ 55], (1) => murathan [ 56], (1) => turhan [ 57], (0) => [ 58], (0) => [ 59], (0) => [ 60], (0) => [ 61], (0) => [ 62], (0) => [ 63], (1) => recep [ 64], (1) => sadi [ 65], (1) => bilal [ 66], (0) => [ 67], (1) => abdullah [ 68], (1) => semsit [ 69], (1) => ercument [ 70], (1) => tijen [ 71], (0) => [ 72], (1) => yurdanur [ 73], (0) => [ 74], (0) => [ 75], (2) => nusret yelda [ 76], (1) => selenay [ 77], (0) => [ 78], (0) => [ 79], (0) => [ 80], (0) => [ 81], (0) => [ 82], (0) => [ 83], (0) => [ 84], (1) => adnan [ 85], (0) => [ 86], (0) => [ 87], (0) => [ 88], (0) => [ 89], (0) => [ 90], (0) => [ 91], (0) => [ 92], (0) => [ 93], (1) => sinem [ 94], (0) => [ 95], (0) => [ 96], (0) => [ 97], (1) => turgut [ 98], (2) => egemen perihan [ 99], (0) => [100], (1) => feraye [101], (0) => [102], (0) => */ std::unordered_set mySet(100); fcs(mySet, 50, rname); std::cout << "Size : " << mySet.size() << "\n"; std::cout << "Bucket Size : " << mySet.bucket_count() << "\n"; std::cout << "Load Factor : " << mySet.load_factor() << "\n"; std::cout << "Load Factor : " << static_cast(mySet.size()) / mySet.bucket_count() << "\n"; std::cout << "Max Load Factor : " << mySet.max_load_factor() << "\n"; for(size_t i{}; i < mySet.bucket_count(); ++i) { std::cout << "[" << std::setw(3) << i << "], "; std::cout << "(" << mySet.bucket_size(i) << ") => "; for(auto iter = mySet.cbegin(i); iter != mySet.cend(i); ++iter) { std::cout << *iter << " "; } std::cout << "\n"; } return 0; } >>>>> Diğer 'Associative' kaplardaki sınıf şablonlarına ek olarak bu sınıf şablonlarının 'Ctor.' fonksiyonları, argüman olarak, 'bucket' sayısı almaktadır. >>>>> Kendi 'custom' türler için 'hash-function' yazılması: * Örnek 1, //.. template inline void hash_combine(std::size_t& seed, const T& val) { seed ^= std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } template inline void hash_val(std::size_t& seed, const T& val) { hash_combine(seed, val); } template inline void hash_val(std::size_t& seed, const T& val, const Types&... args) { hash_combine(seed, val); hash_val(seed, args...); } // Boost kütüphanesindeki 'hash' fonksiyonu: template inline std::size_t hash_val(const Types&... args) { std::size_t seed = 0; hash_val(seed, args...); return seed; } namespace std { template<> // 'explicit specialization' struct hash{ size_t operator()(const Date& date) { /* Boost kütüphanesinin 'hash' fonksiyonu bu tip 'custom' türler için ideal. */ // Alternatif - I return std::hash{}(date.month_day()) + std::hash{}(date.month()) + std::hash{}(date.year()); } }; } int main() { /* # OUTPUT # [17 Eylul 1993 Cuma] => 2019 [17 Eylul 1993 Cuma] => 11093822750367 */ Date keyToHash(17, 9, 1993); std::cout << "[" << keyToHash << "] => " << std::hash{}(keyToHash) << "\n"; // Standart 'std::hash' fonksiyonunun 'Date' sınıfı için // özelleştirilmiş versiyonu çağrıldı. std::cout << "[" << keyToHash << "] => " << hash_val(17, 9, 1993) << "\n"; // 'Boost' kütüphanesindeki versiyonu çağrıldı. std::unordered_set mySet; return 0; } >>> 'std::array' : C dilindeki dizileri sarmalayan sınıf şablonlarıdır. Böylelikle 'array-decay' mekanizmasının yol açacağı problemler önlenmiş olacaktır. Bunun haricinde iki dizi birbirine atanamamaktadır fakat bu sınıf türünden iki obje birbirine atanabilmektedir. 'array-decay', bir dizi isminin bir ifade içerisinde kullanılması sonucu, dizinin ilk elemanının adresine dönüşmesidir. Bir kaç istisnai senaryo haricinde bu mekanizma çalışmamaktadır. Bunlar 'sizeof()' ve '&' operatörlerine operand olmaları, 'decltype' ve 'auto&' ile tür çıkarımında bu mekanizma çalışmaz. * Örnek 1, //.. int main() { int a[10]; // ifade ifadenin türü // &a => int(*)[10] // İlgili mekanizma burada çalışmamıştır. Dizinin ilk elemanının adresine dönüşmemiştir. Çünkü dönüşüm // gerçekleşseydi, 'R-Value Expression' haline gelecekti fakat '&' operatörü 'L-Value Expression' kabul // ettiğinden dolayı sentaks hatası olacaktı. // &a[0] => (int*) // İlgili mekanizma burada çalışmamıştır. Dizinin ilk elemanının adres bilgisini elde ediyoruz. // Array-Decay // int(*)[10] => (int*) // Yukarıdaki ifadelerden: // i. '&a + 1' : Gösterici aritmetiğine göre 'sizeof(int) * 10' kadar artacaktır. Yani ikinci bir on // elemanlı dizi gösterir olacak. // ii. '&a[0] + 1' : Gösterici aritmetiğine göre 'sizeof(int)' kadar artacaktır. Yani bir sonraki // indisteki öğeyi gösterecektir. } Şimdi de sırayla bu sınıfın özelliklerini peyderpey irdeleyelim: >>>> 'Default Init.' gerçekleştirdiğimiz zaman bütün öğeleri çöp değer ile hayata gelirken 'Value Init.' ettiğimiz zaman elemanları 'Zero Init.' ile hayata gelmektedir. * Örnek 1, //.. int main() { /* # OUTPUT # 4 0 703554653 21852 -566558776 0 0 0 0 0 */ std::array arx; // 'Default Init.' std::array ary{}; // 'Value Init.' for(size_t i{}; i < arx.size(); ++i) std::cout << arx[i] << " "; std::cout << "\n"; for(size_t i{}; i < ary.size(); ++i) std::cout << ary[i] << " "; return 0; } >>>> Bu sınıf şablonu bir 'aggregiate type' olduğu için 'aggregiate init.' uygulayabilirim. * Örnek 1, //.. int main() { /* # OUTPUT # 1 3 5 7 9 */ std::array arx{ 1, 3, 5, 7, 9 }; // 'Aggregiate Init.' for(size_t i{}; i < arx.size(); ++i) std::cout << arx[i] << " "; std::cout << "\n"; return 0; } >>>> Standart çıkış akımına yazmak için sınıf şablonunda standart bir üye fonksiyon yoktur. Temsili olarak bizler yazabiliriz: * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } int main() { /* # OUTPUT # [ 1, 3, 5, 7, 9 ] */ std::array arx{ 1, 3, 5, 7, 9 }; // 'Aggregiate Init.' std::cout << arx << "\n"; return 0; } >>>> Sınıfın üye fonksiyonu olan '.data()' ile veya global fonksiyon olan 'data()' fonksiyon ile bu sınıf şablonunu C fonksiyonlarında da kullanabilirim. * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } void printArray(const int* p, size_t size) { while(size--) printf("%d ", *p++); printf("\n"); } int main() { /* # OUTPUT # I: [ 1, 3, 5, 7, 9 ] II: 1 3 5 7 9 III: 1 3 5 7 9 IV: 1 3 5 7 9 */ std::array arx{ 1, 3, 5, 7, 9 }; // 'Aggregiate Init.' std::cout << arx << "\n"; // I printArray(arx.data(), arx.size()); // II printArray(&arx[0], arx.size()); // III printArray(&*arx.begin(), arx.size()); // IV return 0; } >>>> Türleri aynı olmak şartıyla bu türden nesneler de birbirine atanabilmektedir/taşınabilmektedir. * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } int main() { /* # OUTPUT # [ 1, 3, 5, 7, 9 ] [ 0, 0, -74250752, 22029, -1976765216 ] [ 1, 3, 5, 7, 9 ] */ std::array arx{ 1, 3, 5, 7, 9 }; std::cout << arx << "\n"; std::array ary; std::cout << ary << "\n"; ary = arx; // ary = std::move(arx); std::cout << ary << "\n"; return 0; } >>>> Boyutu '0' olan bu türden bir sınıf nesnesi tanımlanabilir. * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } void printArray(const int* p, size_t size) { while(size--) printf("%d ", *p++); printf("\n"); } int main() { /* # OUTPUT # */ int a[0]; printArray(a, 0); std::array ary; std::cout << ary << "\n"; return 0; } >>>> C dizilerini barındırdığı için ekleme ve silme fonksiyonları mevcut değildir. >>>> Sırf bu kaba özel bir üye fonksiyon yoktur. Fakat 'STL' uyumlu bir sınıf şablonudur. * Örnek 1, //.. template void printArray(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; } void printArray(const int* p, size_t size) { printf("[ %d, ", *p); --size; while(--size) printf("%d, ", *++p); printf("%d ]\n", *++p); } int main() { /* # OUTPUT # [ 5, 4, 3, 2, 1 ] [ 10, 9, 8, 7, 6 ] 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < */ int a[5]{ 5, 4, 3, 2, 1 }; printArray(a, 5); std::sort(std::begin(a), std::end(a)); std::array ary{ 10, 9, 8, 7, 6 }; printArray(std::cout, ary); std::sort(ary.begin(), ary.end()); std::copy(std::begin(a), std::end(a), std::ostream_iterator{std::cout, " < "}); std::copy(ary.begin(), ary.end(), std::ostream_iterator{std::cout, " < "}); return 0; } >>>> 'Structural Binding' mekanizması da kullanılabilir. * Örnek 1, //.. int main() { /* # OUTPUT # a : 10 ... e : 6 */ std::array arx{ 10, 9, 8, 7, 6 }; const auto& [ a, b, c, d, e] = arx; std::cout << "a : " << a << "\n...\n"; std::cout << "e : " << e << "\n"; return 0; } * Örnek 2, //.. std::array foo(){ return { 10, 11, 12, 13, 14 }; } int main() { /* # OUTPUT # a : 10 ... e : 14 */ const auto& [ a, b, c, d, e] = foo(); std::cout << "a : " << a << "\n...\n"; std::cout << "e : " << e << "\n"; return 0; } >>>> 'std::tuple' ye ait 'interface' ye de destek vermektedir. * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } int main() { /* # OUTPUT # [ 10, 9, 8, 7, 6 ] [ 40, 10, 7, 14, 2 ] */ std::array arx{ 10, 9, 8, 7, 6 }; std::cout << arx; // tuple-interface std::get<0>(arx) = 40; ++std::get<1>(arx); --std::get<2>(arx); std::get<3>(arx) *= 2; std::get<4>(arx) /= 3; std::cout << arx; return 0; } >>>> İki boyutlu dizi oluşturmak için de kullanılabilir. * Örnek 1, //.. template std::ostream& operator<<(std::ostream& os, std::array& array) { os << "[ " << array.front() << ", "; for(std::size_t i{1}; i < size - 1; ++i) os << array[i] << ", "; os << array.back() << " ]\n"; return os; } int main() { /* # OUTPUT # [ [ 1, 2, 3 ] , [ 4, 5, 6 ] , [ 7, 8, 9 ] , [ 10, 11, 12 ] ] */ std::array, 4> array2D{ { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 } } }; std::cout << array2D; return 0; } >>>> Bu kapta da 'lexicographical_compare' kullanılmaktadır. >> Şimdi de fonksiyon nesnelerine, yani 'functor object' kavramına değinelim. 'functional' başlık dosyasında tanımlanmışlardır. Bu tip sınıf nesnelerinin yerine de 'lambda-expression' kullanabiliriz. * Örnek 1, //.. template struct Less{ constexpr bool operator()(const T& t1, const T& t2)const { return t1 < t2; } }; template struct Greater{ constexpr bool operator()(const T& t1, const T& t2)const { return t1 > t2; } }; template struct Multiplies{ constexpr T operator()(const T& t1, const T& t2)const { return t1 * t2; } }; int main() { /* # OUTPUT # 0 1 2 3 4 6 7 8 9 10 ----------------------------------------------------------------------------- */ std::vector iVec; fc(iVec, 10, Irand{ 0, 10 }); std::sort(iVec.begin(), iVec.end(), Less{}); // std::sort(iVec.begin(), iVec.end(), std::less{}); print(iVec); /* # OUTPUT # 10 9 8 7 6 4 3 2 1 0 ----------------------------------------------------------------------------- */ std::sort(iVec.begin(), iVec.end(), Greater{}); // std::sort(iVec.begin(), iVec.end(), std::greater{}); print(iVec); /* # OUTPUT # 10 11 12 14 15 16 17 18 19 20 ----------------------------------------------------------------------------- */ std::vector iVecTwo; fc(iVecTwo, 10, Irand{ 10, 20 }); print(iVecTwo); /* # OUTPUT # 100 99 96 98 90 64 51 36 19 0 ----------------------------------------------------------------------------- */ std::vector iVecThree(iVecTwo.size()); std::transform(iVec.begin(), iVec.end(), iVecTwo.begin(), iVecThree.begin(), Multiplies{}); // std::transform(iVec.begin(), iVec.end(), iVecTwo.begin(), iVecThree.begin(), std::multiplies{}); print(iVecThree); } Tabii 'STL' içerisinde bazı algoritmalarda vardır ki argüman olarak 'functor object' alırlar. Şöyleki; >>> 'std::invoke' fonksiyon şablonunun incelenmesi: Her türlü 'callable' bu şablon ile çağrılabilmektedir. * Örnek 1, //.. void f1(void) { std::cout << "Myclass::f1(void) was called.\n"; } int f4(void) { std::cout << "Myclass::f4(void) was called. " << 4 << " will be returned.\n"; return 4; } void f2(int value) { std::cout << "Myclass::f2(int " << value << " ) was called.\n"; } int f5(int value) { std::cout << "Myclass::f5(int " << value << " ) was called. " << value * 5 << " will be returned.\n"; return value * 5; } void f3(int value, int valueTwo) { std::cout << "Myclass::f3(int " << value << ", int " << valueTwo << " ) was called.\n"; } int f6(int value, int valueTwo) { std::cout << "Myclass::f6(int " << value << ", int " << valueTwo << " ) was called. " << value * valueTwo << " will be returned.\n"; return value * valueTwo; } int main() { /* # OUTPUT # Myclass::f1(void) was called. Myclass::f4(void) was called. 4 will be returned. Myclass::f2(int 2 ) was called. Myclass::f5(int 5 ) was called. 25 will be returned. Myclass::f3(int 3, int 3 ) was called. Myclass::f6(int 6, int 6 ) was called. 36 will be returned. */ std::invoke(f1); std::invoke(f4); std::invoke(f2, 2); std::invoke(f5, 5); std::invoke(f3, 3, 3); std::invoke(f6, 6, 6); return 0; } * Örnek 2, //.. class Functor{ public: void f1(int value) { std::cout << "Functor::f1(int " << value << " ) was called.\n"; } }; int main() { /* # OUTPUT # Functor::f1(int 31 ) was called. */ std::invoke(&Functor::f1, Functor{}, 31); return 0; } * Örnek 3, //.. class Functor{ public: void f1(int value) { std::cout << "Functor::f1(int " << value << " ) was called.\n"; } }; int main() { /* # OUTPUT # Functor::f1(int 32 ) was called. Functor::f1(int 33 ) was called. Functor::f1(int 34 ) was called. Functor::f1(int 35 ) was called. */ auto funcPtr = &Functor::f1; Functor fx; (fx.*funcPtr)(32); auto fy = new Functor; (fy->*funcPtr)(33); std::invoke(funcPtr, fx, 34); std::invoke(funcPtr, fy, 35); return 0; } * Örnek 4, //.. int main() { /* # OUTPUT # Result : 1296 */ auto f = [](int a){ return a*a; }; std::cout << "Result : " << std::invoke(f, 36) << "\n"; return 0; } >>> 'std::function' sınıf şablonunun incelenmesi: Herhangi bir 'callable' sarmalanmaktadır. * Örnek 1, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int foo( 11 ) was called. 121 will be returned. */ std::function fp; // Geri dönüş değeri 'int' ve 'int' türden bir parametre alan herhangi bir 'callable' ilk değer olarak // verilebilir veya atanabilir. foo(10); fp = foo; fp(11); return 0; } * Örnek 2, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int foo( 11 ) was called. 121 will be returned. int foo( 12 ) was called. 144 will be returned. */ std::function fp; // Geri dönüş değeri 'int' ve 'int' türden bir parametre alan herhangi bir 'callable' ilk değer olarak // verilebilir veya atanabilir. foo(10); fp = foo; fp(11); auto funcPtr = &foo; fp = funcPtr; fp(12); return 0; } * Örnek 3, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } class Functor{ public: int operator()(int value)const { std::cout << "int Functor::operator()( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } }; int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int foo( 11 ) was called. 121 will be returned. int foo( 12 ) was called. 144 will be returned. int Functor::operator()( 13 ) was called. 169 will be returned. */ std::function fp; // Geri dönüş değeri 'int' ve 'int' türden bir parametre alan herhangi bir 'callable' ilk değer olarak // verilebilir veya atanabilir. foo(10); fp = foo; fp(11); auto funcPtr = &foo; fp = funcPtr; fp(12); fp = Functor{}; fp(13); return 0; } * Örnek 4, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } class Functor{ public: int operator()(int value)const { std::cout << "int Functor::operator()( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } }; auto f = [](int value){ std::cout << "Lambda-Expression ( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; }; int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int foo( 11 ) was called. 121 will be returned. int foo( 12 ) was called. 144 will be returned. int Functor::operator()( 13 ) was called. 169 will be returned. Lambda-Expression ( 14 ) was called. 196 will be returned. */ std::function fp; // Geri dönüş değeri 'int' ve 'int' türden bir parametre alan herhangi bir 'callable' ilk değer olarak // verilebilir veya atanabilir. foo(10); fp = foo; fp(11); auto funcPtr = &foo; fp = funcPtr; fp(12); fp = Functor{}; fp(13); fp = f; fp(14); return 0; } * Örnek 5, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } class Functor{ public: int operator()(int value)const { std::cout << "int Functor::operator()( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } }; auto f = [](int value){ std::cout << "Lambda-Expression ( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; }; void theCaller(int value, std::function fp) { fp(value); } int main() { /* # OUTPUT # int foo( 100 ) was called. 10000 will be returned. int Functor::operator()( 101 ) was called. 10201 will be returned. Lambda-Expression ( 102 ) was called. 10404 will be returned. */ theCaller(100, foo); theCaller(101, Functor{}); theCaller(102, f); return 0; } * Örnek 6, //.. auto f = [](int value){ std::cout << "Lambda-Expression ( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; }; class Myclass{ public: int func(int value) { return m_fp(value); } private: std::function m_fp = f; }; int main() { /* # OUTPUT # Lambda-Expression ( 10 ) was called. 100 will be returned. */ Myclass mx; mx.func(10); return 0; } * Örnek 7, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } class Functor{ public: int operator()(int value)const { std::cout << "int Functor::operator()( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } }; auto f = [](int value){ std::cout << "Lambda-Expression ( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; }; int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int Functor::operator()( 11 ) was called. 121 will be returned. Lambda-Expression ( 12 ) was called. 144 will be returned. */ std::vector> funcVec; funcVec.emplace_back(&foo); funcVec.emplace_back(Functor{}); funcVec.emplace_back(f); for(size_t i{}; i < funcVec.size(); ++i) { static int counter{ 10 }; funcVec.at(i)(counter++); } return 0; } * Örnek 8, //.. int foo(int value) { std::cout << "int foo( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } class Functor{ public: int operator()(int value)const { std::cout << "int Functor::operator()( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; } }; auto f = [](int value){ std::cout << "Lambda-Expression ( " << value << " ) was called. " << value * value << " will be returned.\n"; return value*value; }; std::function theCalled(int value) { switch(value) { case 10: return foo; break; case 11: return Functor{}; break; case 12: return f; break; default: return foo; break; } } int main() { /* # OUTPUT # int foo( 10 ) was called. 100 will be returned. int Functor::operator()( 11 ) was called. 121 will be returned. Lambda-Expression ( 12 ) was called. 144 will be returned. */ auto theCalee = theCalled(10); theCalee(10); theCalee = theCalled(11); theCalee(11); theCalee = theCalled(12); theCalee(12); return 0; } * Örnek 9, //.. int main() { /* # OUTPUT # Ben boşum. */ std::function fp; // Herhangi bir 'callable' sarmalanmamıştır. //if(!fp) if(!fp.operator bool()) std::cout << "Ben boşum.\n"; else std::cout << "Ben boş değilim.\n"; return 0; } * Örnek 10, //.. int main() { /* # OUTPUT # Ben boşum. bad_function_call */ std::function fp; // Herhangi bir 'callable' sarmalanmamıştır. //if(!fp) if(!fp.operator bool()) std::cout << "Ben boşum.\n"; else std::cout << "Ben boş değilim.\n"; try { fp(31); } catch(const std::exception& ex) { std::cout << ex.what() << "\n"; } return 0; } >>> 'std::bind' fonksiyon şablonunun incelenmesi: Argüman olarak bir 'callable' ve gerekli argümanları geçiyoruz. Geri dönüş değeri olarak da bir 'function object' alıyoruz. Yani 'operator()()' fonksiyonunu 'overlaod' etmiş bir sınıf türünden nesne döndürülmekte. * Örnek 1, //.. int foo(int valueOne, int valueTwo, int valueThree) { std::cout << "int foo (" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; } class Functor{ public: int operator()(int valueOne, int valueTwo, int valueThree)const { std::cout << "int Functor::operator()(" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; } }; auto f = [](int valueOne, int valueTwo, int valueThree){ std::cout << "Lambda-Expression (" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; }; int main() { /* # OUTPUT # int foo (10, 11, 12) was called. 1320 will be returned. int Functor::operator()(10, 11, 12) was called. 1320 will be returned. Lambda-Expression (10, 11, 12) was called. 1320 will be returned. */ auto fp = std::bind(foo, 10, 11, 12); fp(); auto fpp = std::bind(Functor{}, 10, 11, 12); fpp(); auto fppp = std::bind(f, 10, 11, 12); fppp(); return 0; } * Örnek 2, //.. int foo(int valueOne, int valueTwo, int valueThree) { std::cout << "int foo (" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; } class Functor{ public: int operator()(int valueOne, int valueTwo, int valueThree)const { std::cout << "int Functor::operator()(" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; } }; auto f = [](int valueOne, int valueTwo, int valueThree){ std::cout << "Lambda-Expression (" << valueOne << ", " << valueTwo << ", " << valueThree << ") was called. " << valueOne*valueTwo*valueThree << " will be returned.\n"; return valueOne*valueTwo*valueThree; }; int main() { /* # OUTPUT # int foo (10, 11, 12) was called. 1320 will be returned. int Functor::operator()(10, 11, 12) was called. 1320 will be returned. Lambda-Expression (10, 11, 11) was called. 1210 will be returned. */ auto fp = std::bind(foo, 10, 11, 12); fp(); auto fpp = std::bind(Functor{}, std::placeholders::_1, 11, 12); fpp(10); auto fppp = std::bind(f, std::placeholders::_1, 11, std::placeholders::_2); fppp(10, 11); return 0; } * Örnek 3, //.. void func(int& x, int& y) { ++x; ++y; } int main() { /* # OUTPUT # a / b => 11 / 21 */ int a = 10, b = 20; auto fp = std::bind(func, std::placeholders::_1, std::placeholders::_2); fp(a, b); std::cout << " a / b => " << a << " / " << b << "\n"; return 0; } * Örnek 4, //.. void func(int& x, int& y) { ++x; ++y; } int main() { /* # OUTPUT # a / b => 11 / 21 a / b => 11 / 21 */ int a = 10, b = 20; auto fp = std::bind(func, std::placeholders::_1, std::placeholders::_2); fp(a, b); std::cout << " a / b => " << a << " / " << b << "\n"; // Bu çağrı 'a' ve 'b' değişkenleri üzerinde bir etki oluşturmamıştır. Çünkü bu şekildeki kullanımda 'a' // ve 'b' değişkenlerinin değerleri, 'std::bind' fonksiyonunun geri döndürdüğü sınıf nesnesinin veri // elemanlarına kopyalanmaktadır. Yani bir 'call-by-value' durumu söz konusudur. auto fpp = std::bind(func, a, b); fpp(); std::cout << " a / b => " << a << " / " << b << "\n"; return 0; } * Örnek 5, //.. void func(int& x, int& y) { ++x; ++y; } int main() { /* # OUTPUT # a / b => 11 / 21 a / b => 11 / 21 a / b => 12 / 21 */ int a = 10, b = 20; auto fp = std::bind(func, std::placeholders::_1, std::placeholders::_2); fp(a, b); std::cout << " a / b => " << a << " / " << b << "\n"; auto fpp = std::bind(func, a, b); fpp(); std::cout << " a / b => " << a << " / " << b << "\n"; // Bir 'reference_wrapper' ile sarmaladığımız için 'call-by-reference' durumu söz konusudur ama 'a' için. // 'b' ise hala 'call-by-value'. auto fppp = std::bind(func, std::ref(a), b); fppp(); std::cout << " a / b => " << a << " / " << b << "\n"; return 0; } * Örnek 6, (bkz. https://godbolt.org/z/9M5Kq33nh) //.. void print(std::ostream& os, int value) { os << " value : " << value << "\n"; } int main() { /* # OUTPUT # value : 10 value : 11 */ print(std::cout, 10); // Burada 'std::cout' kopyalamaya kapalı olduğundan 'std::ref()' ile sarmaladık. auto fp = std::bind(print, std::ref(std::cout), std::placeholders::_1); fp(11); return 0; } * Örnek 7, //.. int main() { /* # OUTPUT # void foo(void)const was called. void func(10)const was called. */ Myclass mx; auto fp = std::bind(&Myclass::foo, std::placeholders::_1); fp(mx); auto fpp = std::bind(&Myclass::func, std::placeholders::_1, std::placeholders::_2); fpp(mx, 10); return 0; } * Örnek 8, //.. int main() { /* # OUTPUT # void foo(void)const was called. void func(10)const was called. void foo(void)const was called. void func(11)const was called. */ Myclass mx; auto fp = std::bind(&Myclass::foo, std::placeholders::_1); fp(mx); auto fpp = std::bind(&Myclass::func, std::placeholders::_1, std::placeholders::_2); fpp(mx, 10); auto mxPtr = new Myclass; fp(mxPtr); fpp(mxPtr, 11); return 0; } >>> 'std::generate()' fonksiyonunun incelenmesi: Bir adet 'callable' ile bir adet 'range' argüman olarak alıyor ve ilgili 'range' içerisindeki öğeleri 'callable' vasıtasıyla dolduruyor. * Örnek 1, //.. int main() { /* # OUTPUT # 74 55 57 62 0 98 65 78 17 26 ----------------------------------------------------------------------------- 65_33_20_52_41_ */ std::vector iVec(10); std::generate(iVec.begin(), iVec.end(), []{ return Irand{ 0, 100 }(); }); print(iVec); std::generate_n(std::ostream_iterator{std::cout, "_"}, 5, []{ return Irand{ 0, 100 }(); }); return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # 74 55 57 62 0 98 65 78 17 26 ----------------------------------------------------------------------------- 65_33_20_52_41_ */ std::vector iVec(1000); std::generate(iVec.begin(), iVec.end(), []{ return Irand{ 0, 1000 }(); }); int numberGreaterThan = 500; std::cout << "[" << std::count_if( iVec.begin(), iVec.end(), [numberGreaterThan](int value){ return value > numberGreaterThan; } ) << "] adet rakam " << numberGreaterThan << " rakamından büyüktür.\n"; std::cout << "[" << std::count_if( iVec.begin(), iVec.end(), std::bind(std::greater{}, std::placeholders::_1, numberGreaterThan) ) << "] adet rakam " << numberGreaterThan << " rakamından büyüktür.\n"; return 0; } >>> 'mem_fn' ve 'not_fn' fonksiyon şablonlarının incelenmesi: Bir sınıfın üye fonksiyonunu argüman olarak alıyor ve o üye fonksiyonun çağrılmasını sağlamaktadır, 'mem_fn()' olan. Bir 'function-object' döndürmektedir. 'not_fn()' ise yine bir 'callable' alıyor. Geri dönüş değeri ise, aldığı 'callable' ın geri dönüş değerinin, 'Lojik-Değil' i. * Örnek 1, //.. int main() { /* # OUTPUT # Month : 17 Month : 17 */ Date myDate{ 17, 9, 1993 }; auto fp = std::mem_fn(&Date::month_day); std::cout << "Month : " << myDate.month_day() << "\n"; std::cout << "Month : " << fp(myDate); return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # bennur | candan | seyhan | ferhunde | haluk | ----------------------------------------------------------------------------- 6 | 6 | 6 | 8 | 5 | */ std::vector nameList; fcs(nameList, 5, rname); print(nameList, " | "); std::transform( nameList.begin(), nameList.end(), std::ostream_iterator{std::cout, " | "}, std::mem_fn(&std::string::size) ); return 0; } * Örnek 3, //.. int main() { auto fp = std::not_fn(&prime); std::prime(13); // 'true' değer döndürecektir. fp(13); // 'false' değer döndürecektir. } * Örnek 4, //.. int main() { /* # OUTPUT # 9592 adet asal sayı vardır. 90408 adet asal olmayan sayı vardır. */ std::vector iVec(100'000); std::iota(iVec.begin(), iVec.end(), 0); std::cout << std::count_if(iVec.begin(), iVec.end(), isprime) << " adet asal sayı vardır.\n"; std::cout << std::count_if( iVec.begin(), iVec.end(), std::not_fn(isprime) ) << " adet asal olmayan sayı vardır.\n"; return 0; } * Örnek 5, HATANIN SEBEBİNİ BUL. //.. int main() { /* # OUTPUT # error: no match for call to ‘(std::_Not_fn >) (std::__cxx11::basic_string&)’ // EVDE İNCELE */ size_t length = 5; auto myLambda = [length](const std::string& s){ s.size() == length; }; std::vector sVec(1000); std::generate(sVec.begin(), sVec.end(), []{ return rname() + " " + rfname(); }); std::vector destVec; std::copy_if(sVec.begin(), sVec.end(), std::back_inserter(destVec), std::not_fn(myLambda)); print(destVec, " | "); return 0; } >> 'iota()' : Bir adet 'range' ve bir adet değer almaktadır. İlgili 'range' içerisindeki öğeleri, almış olduğu değer ile başlatıyor ve birer arttırıyor. * Örnek 1, //.. int main() { /* # OUTPUT # 0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_ ----------------------------------------------------------------------------- */ std::vector iVec(15); std::iota(iVec.begin(), iVec.end(), 0); print(iVec, "_"); return 0; } >> 'min_element()', 'max_element()' ve 'minmax_element()' fonksiyonları: Aşağıdaki örneği inceleyelim. Unutmamalıyız ki karşılaştırma kriterini kendimiz de belirleyebiliriz. * Örnek 1, //.. int main() { /* # OUTPUT # [97] => 74 97 75 26 67 0 48 27 80 7 ----------------------------------------------------------------------------- */ std::vector iVec; fcs(iVec, 10, Irand{ 0, 100 }); // Varsayılan olarak 'std::less()' kullanıldı. std::cout << "[" << *std::max_element(iVec.begin(), iVec.end()) << "] => "; print(iVec); return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # [6] => 54 6 19 31 61 78 89 11 30 13 ----------------------------------------------------------------------------- */ std::vector iVec; fcs(iVec, 10, Irand{ 0, 100 }); // Varsayılan olarak 'std::less()' kullanıldı. std::cout << "[" << *std::min_element(iVec.begin(), iVec.end()) << "] => "; print(iVec); return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # [20 / 100] => 83 29 32 20 100 25 39 92 88 33 ----------------------------------------------------------------------------- */ std::vector iVec; fcs(iVec, 10, Irand{ 0, 100 }); std::cout << "[" << *(std::minmax_element(iVec.begin(), iVec.end()).first) << " / " << *(std::minmax_element(iVec.begin(), iVec.end()).second) << "] => "; print(iVec); return 0; } >> 'Structural Binding' : Aşağıdaki örnekleri inceleyelim. * Örnek 1, //.. struct Data{ int m1, m2, m3; }; Data foo(void){ return { 10, 56, 67 }; } int main() { int a[3] = { 1, 4, 7 }; auto [ x, y, z ] = a; // 'x' has the value of a[0] // 'y' has the value of a[1] // 'z' has the value of a[2] Data myData{ 2, 7, 9 }; auto [ xx, yy, zz ] = myData; // 'xx' has the value of myData.m1; // 'yy' has the value of myData.m2; // 'zz' has the value of myData.m3; auto& [ rxx, ryy, rzz ] = myData; // 'rxx' is a reference to myData.m1; // 'ryy' is a reference to myData.m2; // 'rzz' is a reference to myData.m3; auto [ fxx, fyy, fzz ] = foo(); // 'fxx' has the value of '10'; // 'fyy' has the value of '56'; // 'fzz' has the value of '67'; // '[]' içerisindeki değişken sayısındaki uyumsuzluk sentaks hatasıdır. } * Örnek 2, //.. std::tuple foo(); int main() { auto [ x, y, z ] = foo(); // 'x' has the value of the first element in the 'tuple'. // 'y' has the value of the second element in the 'tuple'. // 'z' has the value of the third element in the 'tuple'. } * Örnek 3, //.. int main() { /* # OUTPUT # alev 10 Agustos 1956 Cuma cahide 30 Aralik 1973 Pazar can 15 Kasim 1970 Pazar diana 23 Subat 1997 Pazar ferhat 06 Mayis 1996 Pazartesi gursel 06 Kasim 1996 Carsamba hilmi 17 Aralik 1966 Cumartesi irmak 03 Subat 1957 Pazar nahit 07 Temmuz 2001 Cumartesi naz 10 Haziran 1965 Persembe sefer 16 Temmuz 2000 Pazar tarkan 01 Mayis 1992 Cuma utku 30 Temmuz 2008 Carsamba */ std::map myMap; fcs(myMap, 15, []{ return std::make_pair(rname(), Date::random()); }); std::cout << std::left; for(const auto& [name, birthDate] : myMap) { std::cout << std::setw(12) << name << birthDate << "\n"; } return 0; } * Örnek 4, //.. struct Data{ int x; int y; double d; int a[10]; }; Data foo() { return Data{}; } int main() { auto [ x, y, z, _] = foo(); // İlgili 'Data' sınıfındaki 'a' değişkenini kullanmak istemediğimiz için onun adını '_' olarak verdik. // Fakat '_' ismi görülür olduğu her yerde tekrar bildirilemez. } >> 'Reference Wrapper' sınıf şablonunun incelenmesi: Normalde referanslar 're-bind' edilemiyorlar, bir kapta eleman olarak tutulamıyorlar. İşte bu gibi sıkıntıları bertaraf etmek için oluşturulan ve arka planda bir gösterici tutan sınıf şablonudur. >>> Temsili implementasyonu: * Örnek 1, //.. template class ReferenceWrapper{ public: ReferenceWrapper(T& other) : _mp{&other} {} ReferenceWrapper& operator=(T& other) { _mp = &other; return *this; } operator T& () { return *_mp; } // 'T' hangi türden ise bu fonksiyon ise o türden bir referansa dönüştürecektir, // iş bu sınıf nesnesini. T& get() { return *_mp; } // A getter. private: T* _mp; }; template ReferenceWrapper Ref(T& other) { return ReferenceWrapper{other}; } int main() { int x = 10; ReferenceWrapper r = x; // '_mp' isimli gösterici, 'x' değişkeninin adresini tutmaktadır. // ReferenceWrapper r = x; // CTAD ile tür çıkarımı yapıldı. int y = 20; r = y; // '_mp' isimli gösterici, 'y' değişkeninin adresini tutmaktadır. int z = r; // '_mp' isimli göstericinin gösterdiği değeri, 'z' değişkenine atadık. // int z = r.operator int& (); auto y = Ref(x); } >>> Okuma amaçlı olarak, 'std::ref()' yerine 'std::cref()' sarmalamasını kullanabiliriz. >>> Bünyesindeki fonksiyonların incelenmesi: * Örnek 1, //.. int main() { /* # OUTPUT # r : 133 r : 778 */ int x{ 132 }, y{ 777 }; std::reference_wrapper r{x}; // 'r' demek 'x' demek. ++r; // r.operator int&(); std::cout << "r : " << r.get() << "\n"; r = y; // 'r' demek 'y' demek. ++r; // r.operator int&(); std::cout << "r : " << r.get() << "\n"; return 0; } * Örnek 2, //.. int main() { int x{ 132 }; auto y = x; // 'y' is 'int'. auto z = std::ref(x); // 'z' is 'std::reference_wrapper' return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # a : 10 a : 11 */ int a = 10; func(a); // 'T' is 'int' std::cout << "a : " << a << "\n"; func(std::ref(a)); // 'T' is 'std::reference_wrapper' std::cout << "a : " << a << "\n"; return 0; } * Örnek 4, //.. int main() { /* # OUTPUT # a: 10 d: 2.3 a: 11 d: 1.3 */ int a = 10; double d = 2.3; auto p = std::make_pair( a, d ); ++p.first; --p.second; std::cout << "a: " << a << "\n"; std::cout << "d: " << d << "\n"; auto pRef = std::make_pair( std::ref(a), std::ref(d) ); ++pRef.first; --pRef.second; std::cout << "a: " << a << "\n"; std::cout << "d: " << d << "\n"; return 0; } * Örnek 5, //.. class Biggie{ public: //.. bool operator()(int i)const { return true; } //.. }; const Biggie gBig; std::vector iVecOne; int main() { /* # OUTPUT # */ std::vector iVecTwo; std::copy_if(iVecOne.begin(), iVecOne.end(), std::back_inserter(iVecTwo), ref(gBig)); // Burada son argüman olan 'gBig' referans yolu ile gönderilmeseydi, ilgili 'copy_if' // fonksiyonuna kopyalanacaktı. return 0; } * Örnek 6, //.. int main() { /* # OUTPUT # a : 11 b : 12 c : 13 d : 14 */ int a = 10, b = 11, c = 12, d = 13; // std::vector iVecOne{ a, b, c, d }; // Sentaks hatası. std::vector> iVecOne{ a, b, c, d }; // İlgili kaptaki her bir öğe sırasıyla 'a', 'b', 'c' ve 'd' değişkenlerine referanstır. ++iVecOne.at(0); std::cout << "a : " << a << "\n"; ++iVecOne.at(1); std::cout << "b : " << b << "\n"; ++iVecOne.at(2); std::cout << "c : " << c << "\n"; ++iVecOne.at(3); std::cout << "d : " << d << "\n"; return 0; } * Örnek 7, //.. int main() { /* # OUTPUT # derin derya onat ufuk fadime sezai kamile beril ediz cihan cebrail tarik necmi emine hulusi muslum melisa sefa alican handesu ----------------------------------------------------------------------------- kamilecan alicancan handesucan muslumcan edizcan fadimecan melisacan tarikcan ufukcan eminecan onatcan necmican berilcan derincan sezaican hulusican cebrailcan deryacan cihancan sefacan ----------------------------------------------------------------------------- derincan deryacan onatcan ufukcan fadimecan sezaican kamilecan berilcan edizcan cihancan cebrailcan tarikcan necmican eminecan hulusican muslumcan melisacan sefacan alicancan handesucan ----------------------------------------------------------------------------- */ std::list sList; fcs(sList, 20, rname); print(sList); // Herhangi bir sebepten ötürü bizlerin 'random_access_iterator' kullanma ihtiyacımız olsun: // Artık 'sVec' kabındaki her öğe dolaylı yoldan 'sList' kabındaki öğelere referans. std::vector> sVec{ sList.begin(), sList.end() }; std::shuffle(sVec.begin(), sVec.end(), std::mt19937{ std::random_device{}() }); for(const auto& index : sVec) { std::cout << (index.get() += "can") << " "; } std::cout << "\n------------------------------------------\n"; print(sList); return 0; } >> 'std::tuple' sınıf şablonunun incelenmesi: 'std::tuple' başlık dosyasında bildirilmiştir. 'std::pair' sınıfının çoklusu şeklinde düşünülebilir. Variyadik bir sınıf şablonudur. Yine 'reference_wrapper' gibi bir 'get' arayüzüne sahiptir. İş bu arayüz hem yazma hem okuma amaçlı kullanılabilir. * Örnek 1, //.. int main() { /* # OUTPUT # 0 0 0 10 10.01 10.01 */ std::tuple myTuple; std::cout << get<0>(myTuple) << std::endl; std::cout << get<1>(myTuple) << std::endl; std::cout << get<2>(myTuple) << std::endl; get<0>(myTuple) = 10; get<1>(myTuple) = 10.01f; get<2>(myTuple) = 10.01; std::cout << get<0>(myTuple) << std::endl; std::cout << get<1>(myTuple) << std::endl; std::cout << get<2>(myTuple) << std::endl; int ival = 0; get(myTuple) = 20; std::cout << get<0>(myTuple) << std::endl; // error: the value of ‘ival’ is not usable in a constant expression // Hata kodundan da anlaşılacağı üzere bizim bir 'constant expression' a ihtiyacımız vardır. } * Örnek 2, //.. int main() { /* # OUTPUT # 10 10.01 10.01 */ std::tuple myTuple{ 10, 10.01f, 10.01 }; // std::tuple myTuple{ 10, 10.01f, 10.01 }; // CTAD : 'int', 'float' ve 'double' açılımı gerçekleşecektir. // std::tuple myTuple = { 10, 10.01f, 10.01 }; // CTAD : 'int', 'float' ve 'double' açılımı gerçekleşecektir. std::cout << get<0>(myTuple) << std::endl; std::cout << get<1>(myTuple) << std::endl; std::cout << get<2>(myTuple) << std::endl; } * Örnek 3, //.. constexpr int indexer(int x) { return x; } int main() { /* # OUTPUT # 30 30.03 30.03 */ std::tuple myTuple{ 30, 30.03f, 30.03 }; std::cout << get(myTuple) << std::endl; std::cout << get(myTuple) << std::endl; std::cout << get(myTuple) << std::endl; } * Örnek 4, //.. int main() { /* # OUTPUT # 40 40.04 40.04 */ std::tuple myTuple{ 40, 40.04f, 40.04 }; std::cout << get(myTuple) << std::endl; std::cout << get(myTuple) << std::endl; std::cout << get(myTuple) << std::endl; std::tuple myTupleTwo{ 1, 2, 3 }; std::cout << get(myTupleTwo) << std::endl; // ARTIK SENTAKS HATASI. std::cout << get(myTupleTwo) << std::endl; // ARTIK SENTAKS HATASI. std::cout << get(myTupleTwo) << std::endl; // ARTIK SENTAKS HATASI. } * Örnek 5, //.. using age = int; using wage = double; using name = std::string; int main() { /* # OUTPUT # Age : 31 Wage : 700 Name : Ahmo */ std::tuple workerOne{ 31, 700, "Ahmo" }; std::cout << "Age : " << std::get(workerOne) << "\n"; std::cout << "Wage : " << std::get(workerOne) << "\n"; std::cout << "Name : " << std::get(workerOne) << "\n"; } * Örnek 6, //.. int main() { /* # OUTPUT # Age / Wage / Name => 31 / 700 / 31 */ int age = 31; int wage = 700; std::string name = "Ahmo"; auto workerOne = std::make_tuple( age, wage, name ); std::cout << " Age / Wage / Name => " << std::get<0>(workerOne) << " / " << std::get<1>(workerOne) << " / " << std::get<0>(workerOne) << "\n"; } * Örnek 7, //.. int main() { /* # OUTPUT # Age / Wage / Name => 31 / 700 / Ahmo Age / Wage / Name => 31 / 700 / Ahmo Age / Wage / Name => 31 / 700 / Ahmo Age / Wage / Name => 31 / 700 / Ahmo Age / Wage / Name => 62 / 350 / Ahmo_ */ int age = 31; int wage = 700; std::string name = "Ahmo"; std::cout << " Age / Wage / Name => " << age << " / " << wage << " / " << name << "\n"; auto workerOne = std::make_tuple( age, wage, name ); std::cout << " Age / Wage / Name => " << std::get<0>(workerOne) << " / " << std::get<1>(workerOne) << " / " << std::get<2>(workerOne) << "\n"; std::get<0>(workerOne) *= 2; std::get<1>(workerOne) /= 2; std::get<2>(workerOne) += "_"; std::cout << " Age / Wage / Name => " << age << " / " << wage << " / " << name << "\n"; auto workerTwo = std::make_tuple( std::ref(age), std::ref(wage), std::ref(name) ); std::cout << " Age / Wage / Name => " << age << " / " << wage << " / " << name << "\n"; std::get<0>(workerTwo) *= 2; std::get<1>(workerTwo) /= 2; std::get<2>(workerTwo) += "_"; std::cout << " Age / Wage / Name => " << age << " / " << wage << " / " << name << "\n"; } * Örnek 8, //.. std::tuple workerMaker(void) { return { "Ahmo", 28, 4500 }; } int main() { /* # OUTPUT # */ // Approach - I : Hem yazım zorluğu hem de ilgili 'myWorkerOne' ismi 'scope-leakage' neden olabilir. auto myWorkerOne = workerMaker(); std::string workerName = std::get<0>(myWorkerOne); int workerAge = std::get<1>(myWorkerOne); int workerWage = std::get<2>(myWorkerOne); // Approach - II : 'std::tie()' fonksiyonunu kullanmak: Önce değişkenlerimizi hayata getirdik // sonrasında atama yaptık. std::tie(workerName, workerAge, workerWage) = myWorkerOne; // std::tuple(workerName, workerAge, workerWage) = myWorkerOne; // Approach - III : Tek bir satırda hem değişkenlerimize ilk değer verdik. auto [workerNameTwo, workerAgeTwo, workerWageTwo] = workerMaker(); } * Örnek 9, //.. int main() { /* # OUTPUT # x : 24 y : 4.6 z : name_surname */ int x = 23; double y = 2.3; std::string z = "name"; auto myTuple = std::tie( x, y, z ); ++std::get<0>(myTuple); std::get<1>(myTuple) *= 2; std::get<2>(myTuple) += "_surname"; std::cout << "x : " << x << "\n"; std::cout << "y : " << y << "\n"; std::cout << "z : " << z << "\n"; } * Örnek 10, //.. std::tuple workerMaker(void) { return { "Ahmo", 28, 4500 }; } int main() { /* # OUTPUT # Name : Ahmo Age : 32537 Wage : 4500 Name : Ahmo Age : 28 */ std::string workerName; int workerAge; // 'ignore', 'ignore_t' türünden bir sınıf nesnesi. Bir 'placeholder' std::tie(workerName, ignore, ignore) = workerMaker(); // 'ignore', 'ignore_t' türünden bir sınıf nesnesi. auto [ workerNameTwo, workerAgeTwo, ignore ] = workerMaker(); std::cout << "Name : " << workerName << "\n"; std::cout << "Age : " << workerAge << "\n"; // Çöp değer ile hayata geldi. std::cout << "Wage : " << ignore << "\n"; // 'ignore' nesnesini kullanmamalıyız. std::cout << "\n"; std::cout << "Name : " << workerNameTwo << "\n"; std::cout << "Age : " << workerAgeTwo << "\n"; } * Örnek 11, class MyClass{ public: bool operator<(const MyClass& other) { // Approach - I : Bütün veri elemanlarını tek tek karşılaştırmak return ( age < other.age && wage < other.wage && name < other.name && surname < other.surname ); // Approach - II : Bütün veri elemanlarını bir demet halinde karşılaştırmak return std::tie( age, wage, name, surname ) < std::tie(other.age, other.wage, other.name, other.surname); // Approach - III : C++20 ile dile eklenen 'spaceship' operatörü: } private: int age; int wage; std::string name; std::string surname; }; int main() { //.. } * Örnek 12, //.. using age = int; using wage = float; using name = std::string; using info = std::tuple; int main() { /* # OUTPUT # Age : 58 Wage : 99.99 Name : tempNameSurname Age : 61 Wage : 99.99 Name : tempNameSurname Age : 27 Wage : 99.99 Name : tempNameSurname Age : 40 Wage : 99.99 Name : tempNameSurname Age : 68 Wage : 99.99 Name : tempNameSurname */ std::vector workerVec; for(size_t index{}; index < 5; ++index) { workerVec.emplace_back( rand() % 75, 99.99f, "tempNameSurname" ); } for(const auto& [ _age, _wage, _name ] : workerVec) { std::cout << "Age : " << _age << "\n"; std::cout << "Wage : " << _wage << "\n"; std::cout << "Name : " << _name << "\n"; std::cout << "\n"; } } >> 'bitset' sınıf şablonunun incelenmesi: Aşağıdaki örnekleri inceleyelim: * Örnek 1, //.. int main() { /* # OUTPUT # 0000000000000000 0001110111100011 0110001100000111 */ std::bitset<16> myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' std::bitset<16> myBitSetTwo{ 7651u }; std::cout << myBitSetTwo << "\n"; // İşaretsiz tam sayı parametreli 'Ctor.' std::string name{"0110001100000111000"}; std::bitset<16> myBitSetThree{ name }; std::cout << myBitSetThree << "\n"; // 'std::string' parametreli 'Ctor.' return 0; } * Örnek 2, //.. int main() { /* # OUTPUT # 2 tabanında gösterilecek sayı: -1 2 tabanındaki hali : 11111111111111111111111111111111 */ std::cout << "2 tabanında gösterilecek sayı: "; size_t number; std::cin >> number; std::bitset<32> myBitSet(number); std::cout << "\n2 tabanındaki hali : " << myBitSet << "\n"; return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # 0000000000000000 False... */ std::bitset<16> myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' // Herhangi bir 'bit', 'set' edilmiş ise 'true' döndürecektir. Bütün 'bit' ler '0' ise 'false' döndürecektir. if ( myBitSet.any() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } return 0; } * Örnek 4, //.. int main() { /* # OUTPUT # 0000000000000000 False... True... */ std::bitset<16> myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' // Herhangi bir 'bit', 'set' edilmiş ise 'true' döndürecektir. Bütün 'bit' ler '0' ise 'false' döndürecektir. if ( myBitSet.any() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } // Herhangi bir 'bit', 'set' edilmemiş ise 'true' döndürecektir. Aksi halde 'false' döndürecektir. if ( myBitSet.none() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } return 0; } * Örnek 5, //.. int main() { /* # OUTPUT # 0000000000000000 False... True... False... */ std::bitset<16> myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' // Herhangi bir 'bit', 'set' edilmiş ise 'true' döndürecektir. Bütün 'bit' ler '0' ise 'false' döndürecektir. if ( myBitSet.any() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } // Herhangi bir 'bit', 'set' edilmemiş ise 'true' döndürecektir. Aksi halde 'false' döndürecektir. if ( myBitSet.none() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } // Bütün 'bit' ler, 'set' edilmiş ise 'true' döndürecektir. Aksi halde 'false' döndürecektir. if ( myBitSet.all() ) { std::cout << "True...\n"; } else { std::cout << "False...\n"; } return 0; } * Örnek 6, //.. int main() { /* # OUTPUT # 11111111 bitSize / Set Counter : 8 / 8 */ constexpr int bitSize = 8; std::bitset myBitSet(-1); std::cout << myBitSet << "\n"; // 'Default Ctor.' std::cout << "bitSize / Set Counter : " << bitSize << " / " << myBitSet.count() << "\n"; return 0; } * Örnek 7, //.. int main() { /* # OUTPUT # 00000000 00000001 00000111 11111111 */ constexpr int bitSize = 8; std::bitset myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' myBitSet.set(0); std::cout << myBitSet << "\n"; myBitSet.set(1).set(2); std::cout << myBitSet << "\n"; myBitSet.set(); std::cout << myBitSet << "\n"; return 0; } * Örnek 8, //.. int main() { /* # OUTPUT # 00000000 00000001 00000111 11111111 11111110 11111000 00000000 */ constexpr int bitSize = 8; std::bitset myBitSet; std::cout << myBitSet << "\n"; // 'Default Ctor.' myBitSet.set(0); std::cout << myBitSet << "\n"; myBitSet.set(1).set(2); std::cout << myBitSet << "\n"; myBitSet.set(); std::cout << myBitSet << "\n"; myBitSet.reset(0); std::cout << myBitSet << "\n"; myBitSet.reset(1).reset(2); std::cout << myBitSet << "\n"; myBitSet.reset(); std::cout << myBitSet << "\n"; return 0; } * Örnek 9, //.. int main() { /* # OUTPUT # 00011111 11100000 01100000 00000000 */ constexpr int bitSize = 8; std::bitset myBitSet(31); std::cout << myBitSet << "\n"; // 'Default Ctor.' myBitSet.flip(); std::cout << myBitSet << "\n"; myBitSet.flip(7); std::cout << myBitSet << "\n"; myBitSet.flip(6).flip(5); std::cout << myBitSet << "\n"; return 0; } * Örnek 10, //.. int main() { /* # OUTPUT # 1000 type name : St6bitsetILm4EE type name : NSt6bitsetILm4EE9referenceE 3 is set. */ constexpr int bitSize = 4; std::bitset myBitSet(8); std::cout << myBitSet << "\n"; std::cout << "type name : " << typeid(myBitSet).name() << "\n"; std::cout << "type name : " << typeid(myBitSet[5]).name() << "\n"; if( myBitSet.test(0) ) { std::cout << "0 is set.\n"; } else if( myBitSet.operator[](1) ) { std::cout << "1 is set.\n"; } else if( myBitSet.operator[](2).operator bool() ) { std::cout << "2 is set.\n"; } else { std::cout << "3 is set.\n"; } return 0; } * Örnek 11, //.. int main() { /* # OUTPUT # type name : St6bitsetILm4EE type name : NSt6bitsetILm4EE9referenceE 1000 3 is set. 1101 0 is set. */ constexpr int bitSize = 4; std::bitset myBitSet(8); std::cout << "type name : " << typeid(myBitSet).name() << "\n"; std::cout << "type name : " << typeid(myBitSet[5]).name() << "\n"; std::cout << myBitSet << "\n"; if( myBitSet.test(0) ) { std::cout << "0 is set.\n"; } else if( myBitSet.operator[](1) ) { std::cout << "1 is set.\n"; } else if( myBitSet.operator[](2).operator bool() ) { std::cout << "2 is set.\n"; } else { std::cout << "3 is set.\n"; } myBitSet[2] = myBitSet[3]; myBitSet.set(0); std::cout << myBitSet << "\n"; if( myBitSet.test(0) ) { std::cout << "0 is set.\n"; } else if( myBitSet.operator[](1) ) { std::cout << "1 is set.\n"; } else if( myBitSet.operator[](2).operator bool() ) { std::cout << "2 is set.\n"; } else { std::cout << "3 is set.\n"; } return 0; } * Örnek 12, //.. int main() { /* # OUTPUT # 000000000000000100000110100011013 00000000000010000011010001101000 */ constexpr int bitSize = 32; std::bitset myBitSet(67213u); std::cout << myBitSet << 3 << "\n"; std::cout << ( myBitSet << 3 ) << "\n"; return 0; } * Örnek 13, //.. int main() { /* # OUTPUT # a : 00000000000000010000011010001101 b : 00000000000010111111000000101111 a & b : 00000000000000010000000000001101 a | b : 00000000000010111111011010101111 a ^ b : 00000000000010101111011010100010 */ constexpr int bitSize = 32; std::bitset a(67213u); std::bitset b(782383u); std::cout << "a : " << a << "\n"; std::cout << "b : " << b << "\n"; std::cout << "a & b : " << ( a & b ) << "\n"; std::cout << "a | b : " << ( a | b ) << "\n"; std::cout << "a ^ b : " << ( a ^ b ) << "\n"; return 0; } * Örnek 14, //.. int main() { /* # OUTPUT # 67213 : 00000000000000010000011010001101 268852 : 00000000000001000001101000110100 */ constexpr int bitSize = 32; std::bitset a(67213u); std::cout << a.to_ulong() << " : " << a << "\n"; a <<= 2; std::cout << a.to_ulong() << " : " << a << "\n"; return 0; } * Örnek 15, //.. enum Color { Red, Yellow, Green, NumberOfColors }; int main() { /* # OUTPUT # 0 : 000 1 : 001 1 : 001 1 : 001 */ std::bitset a; std::cout << a.to_ulong() << " : " << a << "\n"; a[Red] = true; std::cout << a.to_ulong() << " : " << a << "\n"; a[Yellow] = ~a[Red]; std::cout << a.to_ulong() << " : " << a << "\n"; a[Green] = a[Yellow]; std::cout << a.to_ulong() << " : " << a << "\n"; return 0; } * Örnek 16, //.. int main() { /* # OUTPUT # 0 : 000 1 : 001 1 : 001 1 : 001 */ std::set> mySet; // error: no match for ‘operator<’ (operand types are ‘const std::bitset<16>’ and ‘const std::bitset<16>’) mySet.insert(16); return 0; } * Örnek 17, //.. int main() { /* # OUTPUT # 00000000 00000001 00000010 00000011 00000100 00000101 00000110 00000111 00001000 00001001 */ std::vector> mySet; for(size_t i{}; i < 10; ++i) mySet.emplace_back(i); for(const auto& index : mySet) std::cout << index << "\n"; return 0; } >> 'Standart Containers Adaptors' : 'stack', 'queue', 'priority_queue' vs. sınıf şablonlarından oluşmaktadırlar. BUNLAR BİR VERİ YAPISI DEĞİLDİR. VERİ YAPISINDA İMPLEMENTASYON DA BİZİ İLGİLENDİRMEKTEDİR. Fakat bu adaptörlerin arka planında vektör mü kullanılmış, bağlı liste mi kullanılmış bizi ilgilendirmemektedir. Yine bunların bir 'container' OLMADIĞINI, sadece ve sadece 'container adaptor' olduğunu da unutmayalım. Çünkü bunlar bir kabı eleman olarak alıyorlar ve onun arayüzünü kendilerine adapte ediyorlar. >>> 'stack' : Arka planda varsayılan kap olarak 'std::deque' kabını kullanmaktadır. Temsili olarak gösterimi aşağıdaki gibidir: * Örnek 1, //.. template> class Stack{ public: typename C::size_type size() const { return _mCon.size(); } T& top() const { return _mCon.back(); } bool empty() const { return _mCon.empty(); } void push(const T& value) { _mCon.push_back(value); } void pop(const T& value) { _mCon.pop_back(value); } template void emplace(Args&& ...args) { _mCon.emplace(args...); } private: C _mCon; }; template using vecStack = std::stack>; int main() { Stack myStack; Stack> myStackTwo; // vecStack myStackTwo; } >>>> Üye Fonksiyonlarının İncelenmesi: * Örnek 1, //.. int main() { /* # OUTPUT # Yığının en üstündeki çıkartılacak öğe: 2 Yığının en üstündeki çıkartılacak öğe: 1 Yığının en üstündeki çıkartılacak öğe: 0 */ std::stack myStack; for(size_t i{}; i < 3; ++i) myStack.push(i); while(!myStack.empty()) { // Yığın boşken bu iki fonksiyonun çağrılması 'Tanımsız Davranış'. std::cout << "Yığının en üstündeki çıkartılacak öğe: " << myStack.top(); myStack.pop(); std::cout << "\n"; } return 0; } * Örnek 2, //.. int main() { std::deque myDeque; //.. // Arka plandaki varsayılan veri yapısı 'deque' olduğundan LEGALDİR. std::stack myStack(myDeque); //.. std::vector myVector; //.. // Arka plandaki varsayılan veri yapısı 'deque' olduğundan SENTAKS HATASIDIR. // error: no matching function for call to ‘std::stack::stack(std::vector&)’ std::stack myStackTwo(myVector); // Arka plandaki varsayılan veri yapısı 'vector' olduğundan LEGALDİR. std::stack> myStackThree(myVector); return 0; } >>>> Bu adaptör 'LIFO' şeklinde, yani son girenin ilk çıktığı, yapılandırılmıştır. >>>> 'stack' bünyesinde kullanılan veri tabanı ilgili sınıf şablonunun 'protected' kısmındadır. Ayrıca 'stack' sınıf şablonundan kalıtım da yapabiliriz. * Örnek 1, //.. class MyStack : public std::stack{ public: void clear() { c.clear; } // Normal şartlarda 'stack' public arayüzünde böyle bir fonksiyon yoktur. Fakat kalıtım yoluyle // yeni bir sınıf elde ettiğimiz zaman, taban sınıfın 'protected' kısmına ulaşarak böyle bir // özellik kazandırabiliriz. Unutmamalıyız ki 'stack' sınıfında sanal bir fonksiyon olmadığından, // bu kalıtım ile 'Run Time Polymorphism' hedeflenmemiştir. Sadece ve sadece arayüzünün // genişletilmesi hedeflenmiştir. }; int main() { std::vector myVec; fcs(myVec, 20, Date::random); print(myVec); return 0; } >>> 'queue' ve 'priority_queue' sınıf şablonları: Her iki sınıf şablonu da 'queue' başlık dosyasında bildirilmiştir. 'FIFO' şeklinde, yani ilk girenin ilk çıktığı, yapılandırılmıştır. Arka planda kullanılan veri yapısının '.pop_front()' şeklinde bir fonksiyonu olmalıdır. İşte bu yüzden bizler arka planda 'std::vector' kullanamayız eğer öğe çıkartmak istiyorsak. Arayüzünde bulunan üye fonksiyonlar, 'stack' sınıf şablonundakiler ile benzerdir. 'queue' sınıfı ekstra bir üye fonksiyonu bulunmamaktadır. >>>> 'priority_queue' sınıf şablonu: >>>>> 'binary_heap' hatırlatması: Yine bir ikili ağaçtır. Her 'parent' iki adet 'child' barındırmaktadır. Fakat, son seviye hariç, her seviyenin tamam olması gerekmektedir. Sadece son seviyenin en sağındaki öğe eksik olabilir. Bu şartı sağlamaz ise 'binary_heap' OLMAYACAKTIR. Vektörel veri yapısını da 'std::make_heap' fonksiyonu ile yukarıdaki hale getirebiliriz. /* 75 / \ 37 42 / \ / \ => 75 37 42 24 26 30 25 17 21 18 12 28 22 24 26 30 25 / \ / \ / \ / \ 17 21 18 12 28 22 Bu görülen ağacımız, son seviye hariç her seviyesi 'complete' olduğundan dolayı, bir 'binary_heap' şeklindedir. Velevki '21' rakamı olmasaydı, seviyelerde bir boşlık olacağından, 'binary_heap' özelliği kaybolacaktır. Bu özelliğe ek olarak her düğüm, altındaki düğümlerden daha yüksek bir değere sahip olmalıdır. 'Binary Search Tree' den farklıdır. */ >>>>>> Örnekler, * Örnek 1, //.. int main() { /* # OUTPUT # 96 199 871 103 133 157 664 901 746 778 814 261 753 244 406 352 12 524 682 15 ----------------------------------------------------------------------------- 901 814 871 746 778 753 664 352 682 96 133 261 157 244 406 103 12 524 199 15 ----------------------------------------------------------------------------- */ /* 901 814 871 746 778 753 664 352 682 96 133 261 157 244 406 103 12 524 199 15 ----------------------------------------------------------------------------- | V 901 814 871 746 778 753 664 352 682 96 133 261 157 244 406 103 12 524 199 15 */ std::vector myVec; fcs(myVec, 20, Irand{ 0, 1000 }); print(myVec); std::make_heap(myVec.begin(), myVec.end()); print(myVec); // Vektörel veri yapısı artık mevcut değil. return 0; } * Örnek 2, int main() { /* # OUTPUT # 798 852 281 477 894 565 322 383 67 281 304 983 144 826 700 290 762 195 610 898 ----------------------------------------------------------------------------- 983 898 826 762 894 565 798 477 610 852 304 281 144 322 700 290 383 195 67 281 ----------------------------------------------------------------------------- 898 894 826 762 852 565 798 477 610 281 304 281 144 322 700 290 383 195 67 983 ----------------------------------------------------------------------------- 898 894 826 762 852 565 798 477 610 281 304 281 144 322 700 290 383 195 67 ----------------------------------------------------------------------------- */ std::vector myVec; fcs(myVec, 20, Irand{ 0, 1000 }); print(myVec); std::make_heap(myVec.begin(), myVec.end()); print(myVec); std::pop_heap(myVec.begin(), myVec.end()); print(myVec); // Ağacın en yukarısındaki ağacın en sonuna gönderildi. myVec.pop_back(); print(myVec); // En sondaki öğe çıkartıldı. return 0; } * Örnek 3, //.. int main() { /* # OUTPUT # I: 618 283 236 863 265 599 694 475 610 453 84 436 593 235 206 521 753 92 743 166 ----------------------------------------------------------------------------- II: 863 753 694 743 453 599 236 521 618 265 84 436 593 235 206 283 475 92 610 166 ----------------------------------------------------------------------------- [863] => 753 743 694 618 453 599 236 521 610 265 84 436 593 235 206 283 475 92 166 863 ----------------------------------------------------------------------------- [753] => 743 618 694 610 453 599 236 521 166 265 84 436 593 235 206 283 475 92 753 ----------------------------------------------------------------------------- [743] => 694 618 599 610 453 593 236 521 166 265 84 436 92 235 206 283 475 743 ----------------------------------------------------------------------------- [694] => 618 610 599 521 453 593 236 475 166 265 84 436 92 235 206 283 694 ----------------------------------------------------------------------------- [618] => 610 521 599 475 453 593 236 283 166 265 84 436 92 235 206 618 ----------------------------------------------------------------------------- [610] => 599 521 593 475 453 436 236 283 166 265 84 206 92 235 610 ----------------------------------------------------------------------------- [599] => 593 521 436 475 453 235 236 283 166 265 84 206 92 599 ----------------------------------------------------------------------------- [593] => 521 475 436 283 453 235 236 92 166 265 84 206 593 ----------------------------------------------------------------------------- [521] => 475 453 436 283 265 235 236 92 166 206 84 521 ----------------------------------------------------------------------------- [475] => 453 283 436 166 265 235 236 92 84 206 475 ----------------------------------------------------------------------------- [453] => 436 283 236 166 265 235 206 92 84 453 ----------------------------------------------------------------------------- [436] => 283 265 236 166 84 235 206 92 436 ----------------------------------------------------------------------------- [283] => 265 166 236 92 84 235 206 283 ----------------------------------------------------------------------------- [265] => 236 166 235 92 84 206 265 ----------------------------------------------------------------------------- [236] => 235 166 206 92 84 236 ----------------------------------------------------------------------------- [235] => 206 166 84 92 235 ----------------------------------------------------------------------------- [206] => 166 92 84 206 ----------------------------------------------------------------------------- [166] => 92 84 166 ----------------------------------------------------------------------------- [92] => 84 92 ----------------------------------------------------------------------------- [84] => 84 ----------------------------------------------------------------------------- */ std::vector myVec; fcs(myVec, 20, Irand{ 0, 1000 }); print(myVec); // I std::make_heap(myVec.begin(), myVec.end()); print(myVec); // II while(!myVec.empty()) { std::pop_heap(myVec.begin(), myVec.end()); std::cout << "[" << myVec.back() << "] => "; print(myVec); myVec.pop_back(); } return 0; } * Örnek 4, //.. int main() { /* # OUTPUT # ata refika efe durmus malik ali caner esen sabriye yasin ----------------------------------------------------------------------------- sabriye refika caner durmus yasin ali efe esen ata malik ----------------------------------------------------------------------------- [sabriye] => refika durmus caner malik yasin ali efe esen ata sabriye ----------------------------------------------------------------------------- [refika] => durmus yasin caner malik ata ali efe esen refika ----------------------------------------------------------------------------- [durmus] => yasin malik caner esen ata ali efe durmus ----------------------------------------------------------------------------- [yasin] => malik esen caner efe ata ali yasin ----------------------------------------------------------------------------- [malik] => caner esen ali efe ata malik ----------------------------------------------------------------------------- [caner] => esen efe ali ata caner ----------------------------------------------------------------------------- [esen] => efe ata ali esen ----------------------------------------------------------------------------- [efe] => ata ali efe ----------------------------------------------------------------------------- [ata] => ali ata ----------------------------------------------------------------------------- [ali] => ali ----------------------------------------------------------------------------- */ std::vector myVec; fcs(myVec, 10, rname); print(myVec); // I std::make_heap(myVec.begin(), myVec.end(), myStringPredicate); print(myVec); // Artık vektörel bir veri yapısı yoktur. Elemanlar bizim kriterimize göre sıralanmışlardır. while(!myVec.empty()) { std::pop_heap(myVec.begin(), myVec.end(), myStringPredicate); std::cout << "[" << myVec.back() << "] => "; print(myVec); myVec.pop_back(); } return 0; } * Örnek 5, //.. int main() { /* # OUTPUT # kaan emine tugra ----------------------------------------------------------------------------- tugra emine kaan ----------------------------------------------------------------------------- [tugra] => emine kaan tugra ----------------------------------------------------------------------------- [XXX] => emine kaan XXX ----------------------------------------------------------------------------- */ std::vector myVec; fcs(myVec, 3, rname); print(myVec); // I std::make_heap(myVec.begin(), myVec.end(), myStringPredicate); print(myVec); // Artık vektörel bir veri yapısı yoktur. Elemanlar bizim kriterimize göre sıralanmışlardır. std::pop_heap(myVec.begin(), myVec.end(), myStringPredicate); std::cout << "[" << myVec.back() << "] => "; print(myVec); myVec.pop_back(); std::cout << "[" << "XXX" << "] => "; myVec.push_back("XXX"); std::push_heap(myVec.begin(), myVec.end(), myStringPredicate); print(myVec); return 0; } >>>>> İşte yukarıdaki örneklerde yapılanları gerçekleştiren sınıf şablonumuz da 'priority_queue' sınıf şablonudur. Bu sınıf şablonumuz ilk parametre olarak tutulacak öğenin cinsi, ikinci parametre olarak arka tarafta kullanılacak veri yapısını ki bu bir 'std::vector' sınıfıdır, üçüncü parametre olarak da karşılaştırma kriterini almaktadır ki bu da 'std::less' sınıf şablonudur. * Örnek 1, //.. int main() { /* # OUTPUT # [13 Ocak 2020 Pazartesi] [27 Nisan 2017 Persembe] [15 Mayis 2013 Carsamba] [25 Mart 2013 Pazartesi] [09 Kasim 2008 Pazar] [05 Eylul 1995 Sali] [24 Nisan 1992 Cuma] [04 Kasim 1977 Cuma] [26 Subat 1970 Persembe] [29 Kasim 1956 Persembe] */ std::priority_queue myQ; for(size_t i{}; i < 10; ++i) myQ.push(Date::random()); while(!myQ.empty()) { std::cout << "[" << myQ.top() << "]\n"; myQ.pop(); } return 0; } * Örnek 2, //.. template using min_queue = std::priority_queue, std::greater>; int main() { /* # OUTPUT # [14 Subat 1953 Cumartesi] [17 Kasim 1953 Sali] [06 Ocak 1975 Pazartesi] [02 Subat 1977 Carsamba] [22 Ocak 1986 Carsamba] [17 Ocak 1992 Cuma] [16 Aralik 2005 Cuma] [28 Haziran 2013 Cuma] [12 Subat 2016 Cuma] [24 Ekim 2019 Persembe] */ min_queue myQ; for(size_t i{}; i < 10; ++i) myQ.push(Date::random()); while(!myQ.empty()) { std::cout << "[" << myQ.top() << "]\n"; myQ.pop(); } return 0; }