diff --git a/C++-OzetNotlar-Ornekler.txt b/C++-OzetNotlar-Ornekler.txt index e0b6512..37b01e8 100644 --- a/C++-OzetNotlar-Ornekler.txt +++ b/C++-OzetNotlar-Ornekler.txt @@ -4235,7 +4235,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- dynamic_cast operatörü sınıflarla ilgili işlem yapmaktadır. Bu operatör çok ileride ele elınacaktır. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ - + /*------------------------------------------------------------------------------------------------------------------------------------------------------------- 32) C++'ta C'deki tür dönüştürme operatörünün sentaks olarak ters biçimini andıran "fonksiyonel tür dönüştürme operatörü" de bulunmaktadır. Fonksiyonel tür dönüştürme operatörü hem sınıflar konusunda uyum oluşturmak için hem de şablon mekanizmasını desteklemek için bulundurulmuştur. Bu operatör C++'ın ilk @@ -4243,7 +4243,7 @@ int main() Fonksiyonel tür dönüştürme operatörü yukarıda da belirttiğimiz gibi adeta klasik tür dönüştürme operatörünün sentaks bakımından tersidir. Yani T bir tür belirtmek üzere C'nin klasik tür dönüştürme operatörü (T)ifade biçimindedir. Fonksiyonel tür dönüştürme operatörü ise T(ifade) biçimindedir. Burada dönüştürülecek türün değil ifadenin paranteze alındığına dikkat ediniz. T(ifade) bir fonksiyon çağrısına benzediği için buna "fonksiyonel tür dönüştürme - operatör" denilmiştir. Örneğin: + operatörü" denilmiştir. Örneğin: int a = 10; int b = 4; @@ -4253,7 +4253,7 @@ int main() c = double(a) / b; Fonksiyonel tür dönüştürme operatörü işlev olarak C'nin tür dönüştürme operatörü gibidir. Yani yine static_cast, const_cast ve reinterpret_cast operatörlerinin - bir birleşmi gibidir. Başka bir deyişle C tarzı tür dönüştürme operatörü ile yapılan hger şey fonksiyonel tür dönüştürmesi ile de yapılabilmektedir. + bir birleşimi gibidir. Başka bir deyişle C tarzı tür dönüştürme operatörü ile yapılan hger şey fonksiyonel tür dönüştürmesi ile de yapılabilmektedir. Fonksiyonel tarzda tür dönüştürmesinin önemli bir sentaks kısıtı vardır. Bu dönüştürmede dönüştürülecek türün tek atomdan oluşması gerekmektedir. Örneğin: @@ -4313,13 +4313,12 @@ int main() return 0; } - /*------------------------------------------------------------------------------------------------------------------------------------------------------------- 14. Ders 02/10/2023 - Pazartesi --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 32) C'de fonksiyonlar ve diziler bütünsel olarak birer nesne kabul edilmektedir. Ancak fonksiyon isimleri bir ifade de kullanıldığında derleyici tarafından + 33) C'de fonksiyonlar ve diziler bütünsel olarak birer nesne kabul edilmektedir. Ancak fonksiyon isimleri bir ifadede kullanıldığında derleyici tarafından otomatik olarak fonksiyonun başlangıç adresine dönüştürülmektedir. Aynı durum C'de diziler için de söz konusudur. Bir dizi ismini biz bir ifadede kullandığımızda derleyici bu dizi nesnesini otomatik olarak dizinin başlangıç adresine (yani ilk elemanın adresine) dönüştürmektedir. O halde C'de bir fonksiyonun ismi ifade içerisinde kullanıldığında onun başlangıç adresini belirtmektedir. Pekiyi fonksiyon çağırma operatörünün operandı ne olmalıdır? C standartlarına göre fonksiyon @@ -4334,7 +4333,7 @@ int main() pf = &foo; C++ tasarım olarak C'yi kapsayacak biçimde oluşturulmuştur. Tabii bu kapsama mutlak değildir. Ancak C++ tasarlanırken özellikle ilk yıllarda C uyumunun korunmasına - çalışılmıştır. Ancak C++'ta referanslar da olduğu için durum biraz daha farklılışamaktadır. C++'ta bir fonksiyon türünden referanslar söz konusu olabilir. Tabii + çalışılmıştır. Fakat C++'ta referanslar da olduğu için durum biraz daha farklılışamaktadır. C++'ta bir fonksiyon türünden referanslar söz konusu olabilir. Tabii böyle referanslar bir fonksiyon ile ilkdeğer verilerek tanımlanmalıdır. Örneğin: void foo() @@ -4354,15 +4353,15 @@ int main() (*f)(); İşte C++'ta fonksiyon referansı ile fonksiyon göstericilerine ilkdeğer verirken aslında doğrudan fonksiyon isimleri kullanılabilmektedir. Ancak genel eğilim - (üye fonksiyon göstericilerinde zorunlı olan durum) fonksiyon göstericilerinde fonksiyonun adresinin programcı tarafından alınmasıdır. Örneğin: + (üye fonksiyon göstericilerinde zorunlu olan durum) fonksiyon göstericilerinde fonksiyonun adresinin programcı tarafından alınmasıdır. Örneğin: void (*pf1)() = foo; // geçerli! void (*pf2)() = &foo; // C++'a göre daha uygun --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 33) C'de önek ++, önek --, =, işlemli atama operatörleri, koşul operatörü sağ taraf değeri üretmektedir. Ancak C++'ta başından beri bu operatörler sol taraf - değeri üretmektedir. Örneğin aşağıdaki ifade C'de geçersiz olduğu halde C++'ta geçerlidir: + 34) C'de önek ++, önek --, =, +=, -=, *= gibi işlemli atama operatörleri, koşul operatörü sağ taraf değeri üretmektedir. Ancak C++'ta başından beri bu + operatörler sol taraf değeri üretirler. Örneğin aşağıdaki ifade C'de geçersiz olduğu halde C++'ta geçerlidir: (a = 10) = 20; // C'de geçersiz C++'ta geçerli @@ -4378,10 +4377,11 @@ int main() Benzer biçimde aşağıda ifade de C'de geçersiz olduğu halde C++'ta geçerlidir: - foo() ? a : b = 10; // C'de geçersiz C++'ta geçerli + (foo() ? a : b) = 10; // C'de geçersiz C++'ta geçerli - Normalde koşul operatörünün operand'ları farklı türlerdense işlem öncesi otomatik tür dönüştürmesi yoluyla aynı türe dönüştürülmektedir. Ancak operatörün - ikinci ve üçüncü operand'ları nesne belirtiyorsa bunların aynı türdne olması gerekmemktedir. Dolayısıyla yularıdaki işlemin eşdeğeri şöyledir: + Normalde koşul operatörünün operand'ları farklı türlerdense işlem öncesi otomatik tür dönüştürmesi yoluyla aynı türe dönüştürülmektedir. Ancak C++'ta koşul + operatörünün ürettiği değere atama yapılıyorsa ve operatörün ikinci ve üçüncü operand'ları nesne belirtiyorsa bunların aynı türden olması zounludur. + Yani yukarıdaki örnekte a ve b aynı türden olmak zorundadır. Yukarıdaki işlemin eşdeğeri şöyledir: if (foo()) a = 10; @@ -4390,8 +4390,8 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 34) C++'a C++11 ile birlikte decltype isimli bir "tür belirleyicisi (type specifier)" eklenmiştir. decltype belirleyicisi parantez içerisinde bir ifade ile - kullanılmaktadır. Genel biçimi şöyledir: + 35) C++'a C++11 ile birlikte decltype isimli bir "tür belirleyicisi (type specifier)" de eklenmiştir. decltype belirleyicisi parantez içerisinde bir ifade + ile kullanılmaktadır. Genel biçimi şöyledir: decltype() @@ -4421,7 +4421,7 @@ int main() decltype(foo()) a; // burada a int türden decltype belirleyicisinde parantezler içerisine bir fonksiyon ismi yazıldığında oluşturulan tür fonksiyon adres türü olmamaktadır. O fonksiyonun türü - olmaktadır. Dolayısıyla eğer parantezler içerisinde bir fonksiyon ismi bulundurulacaksa bu durumda gösaterici ya da referans oluşturmak için dekleratörde * + olmaktadır. Dolayısıyla eğer parantezler içerisinde bir fonksiyon ismi bulundurulacaksa bu durumda gösterici ya da referans oluşturmak için dekleratörde * ya da & atomunun bulundurulması gerekmektedir. Örneğin: decltype(foo) *pf; // burada decltype foo int(int) türünden bir fonksiyon belirtmektedir. @@ -4435,7 +4435,7 @@ int main() Tabii decltype belirleyicinin parantezi içerisinde yazdığımız ifade çalıştırılmamaktadır. Derleyici yalnızca o ifadenin türüyle ilgilenmektedir. - Anımsanacağı gibi fonksiyon parametresi olarak dizi ya da fonksiyon dekleratörü kullanılırsa bu dekleratör gösterici belirtmektedir. Örneğin: + Anımsanacağı gibi fonksiyon parametresi olarak dizi ya da fonksiyon dekleratörü kullanılırsa bu dekleratör gösterici belirtir. Örneğin: void foo(int a[]) { @@ -4526,15 +4526,25 @@ int main() return foo; } - decltype belirleyicisine operand olarak sol taraf değeri oluşturan bir ifade verilirse bu durumda tür sol taraf değeri referansı olmaktadır. Örneğin: + Tabii C++11 ile birlikte zaten fonksiyonların geri dönüş değerlerinde auto tür belirleyicisi kullanılabildiği için biz yukarıdaki fonksiyonu aşağıdaki + gibi de tanımlayabiliriz: + + auto bar() + { + //... + + return foo; + } + + decltype belirleyicisine operand olarak "sol taraf değeri oluşturan bir ifade" verilirse bu durumda tür sol taraf değeri referansı olmaktadır. Örneğin: int a[1]; int b = 10; decltype(a[0]) c = b; - Burada c bir referanstır ve "int &" türündendir. decltype belirleyicisine operand olarak bir değişken ismi verilmesiyle sol taraf değeri oluşturan bir ifade - verilmesi arasındaki farka dikkat ediniz: + Burada c bir referanstır ve "int &" türündendir. Burada decltype belirleyicisinin parantezleri içerisinde operatör içerenbir ifade olduğuna dikkat ediniz. + decltype belirleyicisine operand olarak bir değişken ismi verilmesiyle sol taraf değeri oluşturan bir ifade verilmesi arasındaki farka dikkat ediniz: int a[0]; int b; @@ -4583,7 +4593,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 35) C++11 ile birlikta C++'a NULL adres sabitini temsil eden nullptr anahtar sözcüğü eklenmiştir. Anımsanacağı gibi C'de NULL adres sabiti iki biçimde + 36) C++11 ile birlikta C++'a NULL adres sabitini temsil eden nullptr anahtar sözcüğü de eklenmiştir. Anımsanacağı gibi C'de NULL adres sabiti iki biçimde belirtiliyordu: 1) 0 değerini veren tamsayı türlerine ilişkin sabit ifadeleri @@ -4627,13 +4637,13 @@ int main() } NULL adresin tamsayı 0 ile temsil edilmesi C'nin ilk zamanlarından beri problem yaratan bir durum olmuştur. İşte C++11 ile artık NULL adres için nullptr - isminde bir anahtar sözcü dile eklenmiştir. Örneğin: + isminde bir anahtar sözcük dile eklenmiştir. Örneğin: int *pi; pi = nullptr; // C++11 ve sonrasında izlenmesi gereken iyi teknik - Burada pi göstericisine yine o sistemde derleyicini belirlediği NULL adres atanmaktadır. nullptr anahtar sözcünün C++ standartlarında nullptr_t isimli bir + Burada pi göstericisine yine o sistemde derleyicini belirlediği NULL adres atanmaktadır. nullptr anahtar sözcüğünün C++ standartlarında nullptr_t isimli bir türden olduğu belirtilmektedir. Standartlar nullptr türünü şöyle açıklamıştır: typedef decltype(nullptr) nullptr_t; @@ -4649,9 +4659,9 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 36) typedef anahtar sözcüğü bir bildirime eklenebilir ve bildirimdeki değişkeni o değişkenin türüne ilişkin tür ismi haline getirir. C++11 ile birlikte bazı - nedenlerden dolayı alternatif bir typedef mekanizması da dile eklenmiştir. Buna "type alias" da denilmektedir. Bu alternatif typedef mekanizmasının genel biçimi - şöyledir: + 37) Anımsanacağı gibi typedef anahtar sözcüğü bir bildirime eklenebilir ve bildirimdeki değişkeni o değişkenin türüne ilişkin tür ismi haline getirir. C++11 + ile birlikte bazı nedenlerden dolayı alternatif bir typedef mekanizması da dile eklenmiştir. Buna "type alias" da denilmektedir. Bu alternatif typedef + mekanizmasının genel biçimi şöyledir: using = ; @@ -4699,8 +4709,8 @@ int main() Pekiyi C++11 ile birlikte neden böyle alternatif bir typedef mekanizması eklenmiştir? Öncelikle bu yeni mekanizma daha doğal bir görünüme sahiptir. typedef bildirimleri kişiler tarafından zor öğrenilmektedir. Ancak bu alternatif typedef mekanizmasının dile asıl yerleştirilme gerekçesi şablon konusunda sağladığı - kolaylıklardır. Bu mekanizma şablon mekanizması ile birlikte kullanılabilmektedir. Bu bağlamda çeşitli kolaylıklar sunmaktadır. Şablon mekanizması ileride ele - alınacaktır. + kolaylıklardır. Bu yeni typedef sentaksı şablon mekanizması ile birlikte kullanılabilmektedir. Bu bağlamda çeşitli kolaylıklar sunmaktadır. Şablon mekanizması + ileride ele alınacaktır. decltype belirleyicisi ise typedef ya da using tür bildirimi birlikte de kullanılabilmektedir. Örneğin: @@ -4728,7 +4738,6 @@ int main() return foo; } - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4736,7 +4745,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 37) C++'ta parametre değişkenleri default değer alabilmektedir. Fonksiyon çağrılırken default değer alan parametre değişkenleri için argüman girilmeyebilir. + 38) C++'ta parametre değişkenleri default değer alabilmektedir. Fonksiyon çağrılırken default değer alan parametre değişkenleri için argüman girilmeyebilir. Bu durumda sanki argüman olarak o default değerlerin girilmiş olduğu kabul edilir. Eğer default değer alan parametre değişkenleri için argüman girilmişse bu durumda bu default değerler dikkate alınmaz. Örneğin: @@ -4760,9 +4769,9 @@ int main() foo(100, 200); - Bu çağrının makine kodlarına bakıldığında aslında c paarametre değişkeni için 20 değerinin akratıldığı görülecektir. Yani verilen default değerlerin aktarımı - konusunda bir etkinlik söz konusu değildir. Buradaki fonksiyon her zaman üç parametreye sahiptir ve çağrım sırasında her zaman bu üç parametre için üç argüman - aktarılmaktadır. Yukarıdaki fonksiyonu aşağıdaki gibi çağırmış olalım: + Bu çağrının makine kodlarına bakıldığında aslında c parametre değişkeni için 20 değerinin akratıldığı görülecektir. Yani verilen default değerlerin aktarımı + konusunda bir etkinlik farkı söz konusu değildir. Buradaki fonksiyon her zaman üç parametreye sahiptir ve çağrım sırasında her zaman bu üç parametre için üç + argüman aktarılmaktadır. Yukarıdaki fonksiyonu aşağıdaki gibi çağırmış olalım: foo(100); @@ -4795,15 +4804,15 @@ int main() } Burada yukarıda belirttiğimiz kurala uyulmamıştır. a parametre değişkeni default değer aldığı için onun sağındakilerin hepsinin default değer alması gerekirdi. - Bu kural şöyle ifade edilebilir: Default değer alan parametre değişkenleri parametre listesinin sağında birikmiş olmalıdır. Eğer yukarıdaki gibi bir durum + Bu kural şöyle de ifade edilebilir:"" Default değer alan parametre değişkenleri parametre listesinin sağında birikmiş olmalıdır". Eğer yukarıdaki gibi bir durum geçerli olsaydı aşağıdaki gibi bir sentaksın da geçerli olması gerekirdi: foo(100, , 200); // böyle bir sentaks yok! Halbuki böyle bir sentaks yoktur. - Parametre değişkenlerine verilen ilkdeğerler tipik olarak sabit ifadesi olmaktadır. Ancak global değişkenler ve hatta fonksiyonlar da bu ilkdeğer vermede - kullanılabilmektedir. (Sınıflar konusuna geldiğimizde üye fonksiyonlar için sınıfın veri elemanları da ilkdeğer vermede kullanılabilmektedir.) + Parametre değişkenlerine verilen ilkdeğerler tipik olarak sabit ifadesi olsalar da bu zorunlu değildir. Global değişkenler ve hatta fonksiyonlar da bu ilkdeğer + vermede kullanılabilmektedir. (Sınıflar konusuna geldiğimizde üye fonksiyonlar için sınıfın veri elemanları da ilkdeğer vermede kullanılabilmektedir.) Örneğin: int g_x = 10; @@ -4867,7 +4876,6 @@ int main() foo(); Buradaki çağrının da eşdeğeri yukarıdaki gibi olduğuna göre o anca g_x global değişkeninin değeri kullanılacağından a parametre değişkenine 21 kopyalanacaktır. - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -4933,7 +4941,6 @@ int main() { //... } - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -4964,7 +4971,7 @@ void dispmsg(const char *msg) return a + b; } - Burada verilen default değerlerin diğer değerlerden hiçbir farkıyoktur. Dolayısıyla böyle bir kullanım kötü bir tekniktir. Bu biçimde verilen ilkdeğerler bir + Burada verilen default değerlerin diğer değerlerden hiçbir farkı yoktur. Dolayısıyla böyle bir kullanım kötü bir tekniktir. Bu biçimde verilen ilkdeğerler bir anlam ifade etmediği gibi kodu inceleyenleri de yanlı yönlendirebilmektedir. Fakat örneğin: void disp_number(int a, int base = 10); @@ -4978,8 +4985,8 @@ void dispmsg(const char *msg) --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Bazen parametre değişkenine verilen default değer aslında o parametre değişkeni için argüman girilmediğini tespit etmekte kullanılmaktadır. Programcı asıl - default değeri başka bir biçimde elde ediyor olabilmektedir. Örneğin: + Bazen parametre değişkenlerine verilen default değerler aslında o parametre değişkenleri için argüman girilmediğini tespit etmekte kullanılmaktadır. Programcı + asıl default değerleri başka bir biçimde elde ediyor olabilir. Örneğin: void disp_date(int day = 0, int month = 0, int year = 0); @@ -5006,7 +5013,6 @@ void dispmsg(const char *msg) } Burada eğer ilgili parametre için argüman girilmemişse o anda içinde bulunulan tarihe ilişkin değer kullanılmıtr. - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -5044,7 +5050,7 @@ int main() } /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 38) Anımsanacağı gibi C'de statik ömürlü (yani global ve statik yerel) nesnelere verilen ilkdeğerlerin sabit ifadesi olması zorunludur. Örneğin aşağıdaki gibi + 39) Anımsanacağı gibi C'de statik ömürlü (yani global ve statik yerel) nesnelere verilen ilkdeğerlerin sabit ifadesi olması zorunludur. Örneğin aşağıdaki gibi bir global değişken tanımalması C'de geçerli değildir: int square(int a) @@ -5140,7 +5146,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 39) C'de fonksiyon tanımlamada parametre değişkenlerine isim verilmesi zorunludur. Ancak C++'ta böyle bir zorunluluk yoktur. Örneğin: + 40) C'de fonksiyon tanımlarken parametre değişkenlerine isim verilmesi zorunludur. Ancak C++'ta böyle bir zorunluluk yoktur. Örneğin: void foo(int) // C'de geçersiz, C++'ta geçerli { @@ -5173,13 +5179,12 @@ int main() //... } - Parametre değişkenlerine isim verilmemesi onların programcı tarafından kullanılamamasına yol açmaktadır. Pekiyi neden programcı böyle bir şeyi tercih etsin? + Parametre değişkenlerine isim verilmemesi onların programcı tarafından kullanılamamasına yol açar. Pekiyi neden programcı böyle bir şeyi tercih etsin? İşte bazen programcının aynı parametrik yapıya sahip birden fazla fonksiyon tanımlaması gerekebilmektedir. Bu tür durumlarda kullanılmayan ama sözde (dummy) bir parametre değişkenine gereksinim duyulmaktadır. Sonraki paragrafta fonksiyonların overload edilmeleri konusu ele alınmaktadır. Tabii mademki isim verilmemiş parametre değişkenleri aslında fonksiyon tarafından kullanılamamaktadır. O halde optimizasyon sırasında derleyici o parametre - değişkeni için bazı koşullar da sağlanıyorsa agrüman parametre aktarımını hiç yapmayabilir. - + değişkeni için bazı koşullar da sağlanıyorsa argüman parametre aktarımını hiç yapmayabilir. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -5187,10 +5192,10 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 40) C++'ta parametrik yapıları farklı olmak koşuluyla aynı faaliyet alanında aynı isimli birden fazla fonksiyon bulunabilir. Bu duruma İngilizce "function + 41) C++'ta parametrik yapıları farklı olmak koşuluyla aynı faaliyet alanında aynı isimli birden fazla fonksiyon bulunabilir. Bu duruma İngilizce "function overloading" denilmektedir. Halbuki C'de hiçbir durumda aynı isimli birden fazla fonksiyon bulunamamaktadır. Parametrik yapıların farklı olması demek parametrelerin türce veya sayıca farklı olması demektir. Parametre değişkenlerinin isimlerinin ve fonksiyonların geri dönüş değerlerinin türlerinin bu bağlamda bir önemi yoktur. - Önemli olan parametre değişkenlerinin türlerinin ya da sayılarının farklı olmasıdır. Örneğin aşağıdaki foo fonksiyonları C++'ta birlikte bulunabilir: + Önemli olan parametre değişkenlerinin ""türlerinin ya da sayılarının"" farklı olmasıdır. Örneğin aşağıdaki foo fonksiyonları C++'ta birlikte bulunabilir: void foo(int a) { @@ -5299,8 +5304,20 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- + const ve volatile niteleyicileri semantik bakımdan tamamen farklı olsa da sentaks bakımından birbirlerine çok benzemektedir. Bu nedenle C++ standartlarında + bu iki niteleyiciye "cv niteleyicileri (cv qualifiers)" da denilmektedir. Nesnenin kendisini niteleyen cv niteleyicileri "üst düzey (top level)" cv niteleyicileri + olarak da isimlendirilmektedir. Örneğin: + + const int a = 10; + + Burada const niteleyicisi nesnenin kendisini const yaptığı için üst düzey niteleyicidir. Fakat örneğin: + + const int *pi; + + Burada const niteleyicisi nesnenin kendisini değil göstericinin gösterdiği yeri const yapmaktadır. Dolayısıyla üst düzey değildir. + Türlerdeki "üst düzey const ve volatile niteleyicileri (top level cv qualifiers)" türleri farklılaştırmamaktadır. Yani fonksiyonun imzası oluşturulurken üst - düzey const ve volatile niteleyicileri atılmaktadır. Üst düzey const ve volatile niteleyicileri "nesnenin kendisini niteleyen" const ve volatile niteleyicileridir. + düzey (top level) const ve volatile niteleyicileri atılmaktadır. Örneğin: const int a = 10; // buradaki const üst düzey @@ -5380,13 +5397,13 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Amaç dosya formatlarında (object file formats) aynı isimli birden fazla global sembol bulunamamaktadır. Bu durumda C++ derleyicileri overload edilmiş - fonksiyonların isimlerini mecburen onların parametre türleriyle kombine ederek amaç dosyaya yazmaktadır. Genel olarak bir global sembolün isminin değiştirilerek - amaç dosyaya yazılmasına İngilizce "name decoration" ya da "name mangling" denilmektedir. Böylece programcı fonksiyonlara aynı isimleri vermiş olsa da aslında - C++ derleyicisi onları farklı isimlerle amaç dosyaya yazmaktadır. C++ standartları "name decoration" konusunda herhangi bir belirlemede bulunmamıştır. Yani - bu konu tamamen derleyicileri yazanların isteğine bırakılmıştır. Derleyiciler arasında "name decoration" bakımından önemli farklılıklar bulunabilmektedir. + fonksiyonların isimlerini mecburen onların parametre türleriyle kombine ederek amaç dosyaya yazar. Genel olarak bir global sembolün isminin değiştirilerek + amaç dosyaya yazılmasına " isim dekorasyonu (name decoration)" ya da "name mangling" denilmektedir. Böylece programcı fonksiyonlara aynı isimleri vermiş olsa + da aslında C++ derleyicileri onları farklı isimlerle amaç dosyaya yazmaktadır. C++ standartları "name decoration" konusunda herhangi bir belirlemede bulunmamıştır. + Yani bu konu tamamen derleyicileri yazanların isteğine bırakılmıştır. Derleyiciler arasında "name decoration" bakımından önemli farklılıklar bulunabilmektedir. Genellikle C++ programcılarının bu konunun ayrıntılarını bilmesine gerek yoktur. Ancak sembolik makine dilinde C++ için fonksiyon yazanların mecburen bu durumun - farkında olması gerekmektedir. Aslında "name decoration" C'de uygulanabilmektedir. Örneğin Microsoft C derleyicileri global sembollerin başın "_" öneki getirerek - onu amaç koda yazmaktadır. Yine 32 bit sistemlerde Microsoft değişik "fonksiyon çağırma biçimlerine (calling convention)" değiik isim dekorasyonu kullanmaktadır. + farkında olması gerekir. Aslında "name decoration" C'de de uygulanabilmektedir. Örneğin Microsoft C derleyicileri global sembollerin başın "_" öneki getirerek + onları amaç koda yazar. Yine 32 bit sistemlerde Microsoft değişik "fonksiyon çağırma biçimlerine (calling convention)" göre değişik isim dekorasyonu kullanmaktadır. Aşağıdaki bağlantıda Microsoft C++ derleyicilerinin uyguladığı isim dekoroasyonu hakkında ayrıntılı bilgiler verilmektedir: @@ -5397,7 +5414,7 @@ int main() https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling Microsoft dekore edilmiş isimlerin aslında hangi parametrik yapıya sahip fonksiyonlara karşılık geldiğini anlayabilmek için "undname" isimi bir utility - program da bulundurmaktadır. Örneğin: + program da bulundurmuştur. Örneğin: F:\Dropbox\Kurslar\SysProg-1\Src>undname ?foo@@YAXHH@Z Microsoft (R) C++ Name Undecorator @@ -5437,7 +5454,7 @@ int main() foo(10); - 10 int türdne bir sabittir. Dolayısıyla argüman int türdendir. O halde int paranetreye sahip olan foo fonksiyonu çağrılacaktır. Örneğin: + 10 int türden bir sabittir. Dolayısıyla argüman int türdendir. O halde int paranetreye sahip olan foo fonksiyonu çağrılacaktır. Örneğin: foo(3.14); @@ -5488,18 +5505,18 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Pekiyi ya çağrılma ifadesindeki argümanların türleriyle tam bir uyuşma gösteren fonksiyon yoksa ne olacaktır? İşte bu durumda iki sonuç oluşabilir. Birincisi - çağırma işlemi geçersiz olabilir. Yani çağırma error ile sonuçlanır. İkincisi mevcut aynı isimli fonksiyonlardan bir tanesi "kötünün iyisi" olarak seçilir. - Bu noktada "overload resolution" sürecinin ayrıntıları devreye girmektedir. İzleyen paragraflarda ayrıntılı kurallar açıklanacaktır. + çağırma işlemi geçersiz olabilir. Yani çağırma error ile sonuçlanır. İkincisi aynı isimli fonksiyonlardan bir tanesi "kötünün iyisi" olarak seçilir. Bu noktada + "overload resolution" sürecinin ayrıntıları devreye girmektedir. İzleyen paragraflarda ayrıntılı kurallar açıklanacaktır. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Derleyici tarafından yapılan dönüştürmelere "otomatik tür dönüştürmeleri (implicit type conversions)" denilmektedir. Overload resolution sürecinin ayrıntılarını anlayabilmek için otomatik (implicit) tür dönüştürmeleri arasındaki kalite farklılıklarının bilinmesi gerekir. - T1, T2 ve T3 birer tür belirtmek üzere T1 -> T3 dönüştürmesi ile T2 -> T3 dönüştürmesi arasında bir iyiilk kıyaslaması yapıldığında üç durum söz konusu olabilir: + T1, T2 ve T3 birer tür belirtmek üzere T1 -> T3 dönüştürmesi ile T2 -> T3 dönüştürmesi arasında bir iyiilik kıyaslaması yapıldığında üç durum söz konusu olabilir: 1) T1 -> T3 dönüştürmesi T2 -> T3 dönüştürmesinden daha iyi olabilir. - 2) T2 -> T3 dönüştürmesi ile T1 -> T3 dönüştürmesinden daha iyi olabilir + 2) T2 -> T3 dönüştürmesi T1 -> T3 dönüştürmesinden daha iyi olabilir 3) T1 -> T3 dönüştürmesi ile T2 -> T3 dönüştürmelerinden hiçbiri diğerinden iyi ya da kötü olmayabilir. Örneğin: @@ -5517,14 +5534,15 @@ int main() Otomatik dönüştürmeler arasındaki kalite farklılıkları iyiden kötüye doğru şöyle sıralanmaktadır: - 1) Tam Uyum (Exact Match): Üst düzet const ve volatile belirleyicileri atıldıktan sonra iki tür birbirinin aynısı ise tam uyum söz konusudur. Örneğin: + 1) Tam Uyum (Exact Match): Üst düzey (top level) const ve volatile belirleyicileri atıldıktan sonra iki tür birbirinin aynısı ise tam uyum söz konusudur. + Örneğin: int -> int const int -> int int -> volatile int double -> double - 2) Int Türüne Yükseltme Dönüştürmesi (Ineteger Promotion Conversion) ya da Double Türüne Yükseltme Dönüştürmesi (Floating Point Promotion): Bilindiği gibi + 2) int Türüne Yükseltme Dönüştürmesi (Ineteger Promotion Conversion) ya da Double Türüne Yükseltme Dönüştürmesi (Floating Point Promotion): Bilindiği gibi int türünden küçük olan türlerin int türüne dönüştürülmesine standartlarda "integer promotion" ya da "integral promotion" denilmektedir. Benzer biçimde float türünden double türüne dönüştürmeye de "floating point promotion" denilmektedir. Örneğin: @@ -5569,7 +5587,7 @@ int main() Burada da iki dönüştürme aynı kalitededir. - Java ve C# ile C++ bu bakımdan farklılıklar göstermektedir. Örneğin bu dillerde int -> long dönüştürmesi shprt -> lng dönüştürmesinden daha iyi kabul edilmektedir. + Java ve C# ile C++ bu bakımdan farklılıklar göstermektedir. Örneğin bu dillerde int -> long dönüştürmesi short -> lng dönüştürmesinden daha iyi kabul edilmektedir. Bu dillerden geçenler bu durumlara dikkat etmemlidirler. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ @@ -6137,18 +6155,18 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 41) C++'ta global "isim kirliliğini (name pollution)" engellemek için "isim alanları (namespaces)" denilen C'de olmayan bir özellik bulunmaktadır. Farklı firma - ya da kurumların kütüphanelerinin bir arada kullanıldığı projelerde "isim çakışmaları (name collisions)" oluşabilmektedir. Örneğin A firmasının kütüphanesi + 42) C++'ta global "isim kirliliğini (name pollution)" engellemek için "isim alanları (namespaces)" denilen C'de olmayan bir özellik bulunmaktadır. Farklı + firma ya da kurumların kütüphanelerinin bir arada kullanıldığı projelerde "isim çakışmaları (name collisions)" oluşabilmektedir. Örneğin A firmasının kütüphanesi ile B firmasının kütüphanesini birlikte kullanırken bu iki firma tesadüfen bir yapı ya da sınıfa aynı isimleri vermiş olabilirler. Bu önemli bir problemdir. - Biz her iki firmanın kütüphanelerini kullanmak için onların başlık dosyalarını include ettiğimizde isim çalışması nedeniyle error'ler oluşacaktır. İşte isim - alanları bu problemleri ortadan kaldırmak için düşünülmüştür. Örneğin A firmasının kütüphanesi ile B firmasının kütüphanesini birlikte kullanmak isteyelim. - Bunun için iki firmanın oluşturduğu başlık dosyalarını include edelim: + Biz her iki firmanın kütüphanelerini kullanmak için onların başlık dosyalarını include ettiğimizde isim çakışması nedeniyle error'ler oluşacaktır. İşte isim + alanları bu problemleri büyük ölçüde (tamamen değil) ortadan kaldırmak için düşünülmüştür. Örneğin A firmasının kütüphanesi ile B firmasının kütüphanesini + birlikte kullanmak isteyelim. Bunun için iki firmanın oluşturduğu başlık dosyalarını include edelim: #include "a.hpp" #include "b.hpp" İki firma da tesadüfen bir yapıya aynı ismi vermiş olabilir. Bu durumda derleme sırasında error oluşacaktır. Bu error'leri pratik bir biçimde düzeltmek de - mümkün değildir. Çünkü kütüphane derlenmiştir ve oradaki isimler object modüllere çoktan yazılmıştır. Yani çakışan isimleri başlık dosyasında dğeiştirmek + mümkün değildir. Çünkü kütüphane derlenmiştir ve oradaki isimler object modüllere çoktan yazılmıştır. Yani çakışan isimleri başlık dosyasında değiştirmek bir fayda sağlamayacaktır. Bir isim alanı bildiriminin genel biçimi şöyledir: @@ -6179,7 +6197,7 @@ int main() Her kurum global isimlerini kendine özgü bir isim alanı içerisinde oluşturursa global isim kirliliği büyük ölçüde (ama tamamen değil) engellenmiş olmakradır. İsim alanına benzer benzer kavramlar pek çok yeni dilde de bulunmaktadır. Örneğin bu kavram Java'da "paket (package)", Python'da "modül (module)" ismiyle - karşımıza çıkmaktadır. C# her ne kadar büyük ölçüde Java'dan kopya çekilmişse de kendini C++'a dah fazla yaklaştırmıştır. C# C++'ın isim alanlarını basitleştirerek + karşımıza çıkmaktadır. C# her ne kadar büyük ölçüde Java'dan kopya çekilmişse de kendini C++'a daha fazla yaklaştırmıştır. C# C++'ın isim alanlarını basitleştirerek bünyesine katmıştır. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ @@ -6281,7 +6299,7 @@ int main() } /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - İç bir isim alanı C++17 ile birlikte artık hamlede bildirilebilmektedir. Örneğin: + İç bir isim alanı C++17 ile birlikte artık hamlede de bildirilebilmektedir. Örneğin: namespace A::B::C { @@ -6330,8 +6348,8 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Bir isim alanı ismi dışarıdan çözünürlük operatörü ile isim alanı ismini belirterek kullanabiliyorduk. Ancak aynı isim alanı içerisindeki bir isim doğrudan - kullanılabilmektedir. Örneğin: + Bir isim alanı içerisindeki ismi dışarıdan çözünürlük operatörü ile isim alanı ismini belirterek kullanabiliyorduk. Ancak aynı isim alanı içerisindeki bir + isim doğrudan kullanılabilmektedir. Örneğin: namespace CSD { @@ -6345,7 +6363,7 @@ int main() Burada foo fonksiyonu g_x değişkenini hiç niteliklendirmeden doğrudan kullanabilmektedir. Çünkü foo fonksiyonunun kendisi de aynı isim alanı içeisindedir. Niteliksiz isim arama (unqualified name lookup) sırasında isim alanlarına içten dışa doğru bakılmaktadır. Dolayısıyla iç bir isim alanı onun dışındaki isimleri - de niteliklendirmeden kullanabilir. İsim aramsı (name lookup) ayrıntıları ileride ele alınacaktır. Örneğin: + de niteliklendirmeden kullanabilir. İsim aramasının (name lookup) ayrıntıları ileride başka bir bölümde ele alınacaktır. Örneğin: namespace CSD { @@ -6403,7 +6421,7 @@ int main() } } - Burada foo ve bar fonksiyonlarının her ikisi de CSD isim alanı içerisindedir. Yani isimn alanları tek parça olarak yazılmak zorunda değildir. Tabii aynı isim + Burada foo ve bar fonksiyonlarının her ikisi de CSD isim alanı içerisindedir. Yani isim alanları tek parça olarak yazılmak zorunda değildir. Tabii aynı isim alanı içerisinde aynı isimli tek bir değişken tanımlamabilir. Örneğin: namespace CSD @@ -6463,8 +6481,29 @@ int main() //... } - Buradaki B isim alanları tamamen farklı isim alanlarıdır. Dolayısıyla birleştirilmezler. Aynı isim alanlarının içindeki aynı isimli isim alanalrı - birleştirilmektedir. + Buradaki B ismindeki iki isim alanı tamamen farklı isim alanıdır. Dolayısıyla birleştirilmezler. Aynı isim alanlarının içindeki aynı isimli isim alanları + birleştirilmektedir. Örneğin: + + namespace A::B + { + void foo() + { + cout << "A::B::foo" << endl; + } + } + + namespace A + { + namespace B + { + void bar() + { + cout << "A::B::bar" << endl; + } + } + } + + Burada B isim alanları birleştirilir. Çünkü her iki B de A isim alanı içerisindedir. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -6499,7 +6538,7 @@ int main() } /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Hiçbir isim alanının içerisinde olmayan global bölge de bir isim alanı belirtmektedir. O bölgeye "global isim alanı (global namespace)" denilmektedir. + Hiçbir isim alanının içerisinde olmayan global bölge de bir isim alanı belirtmektedir. Bu bölgeye "global isim alanı (global namespace)" denilmektedir. Örneğin: namespace CSD @@ -6523,7 +6562,6 @@ int main() Burada bar fonksiyonu global isim alanı içerisindedir. CSD isim alanı da global isim alanı içerisindedir. Her isim alanınının doğrudan ya da dolaylı olarak global isim alanı içerisinde olduğuna dikkat ediniz. main fonksiyonu da global isim alanı içerisindedir. İsim alanlarını bir dizin (directory) ağacına benzetirsek global isim alanını kök dizine benzetebiliriz. - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -6582,10 +6620,9 @@ int main() } } - Burada prototip bildirimi CSD::Util içerisinde yapılmış, tanımlama onu kapsayan CSD isim alanı alnı içerisinde yapılmıştır. Tabii tanımlamanın hemen bir - yukarıdaki isim alanı içerisinde yapılması zorunlu değildir. Kapsayan herhangi bir isim alanı içerisinde yapılabilir. Örneğin: - - + Burada prototip bildirimi CSD::Util içerisinde, tanımlama ise onu kapsayan CSD isim alanı alnı içerisinde yapılmıştır. Tabii tanımlamanın hemen bir + yukarıdaki isim alanı içerisinde yapılması zorunlu değildir. Kapsayan herhangi bir isim alanı içerisinde niteliklendirmelerle de yapılabilir. Örneğin: + namespace CSD { namespace Util @@ -6690,7 +6727,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Bir fonksiyon çağrıldığında önce isim araması yapılır. İsim bulunursa ismin bulunduğu faaliyet alanındaki aynı isimli fonksiyonlar aday fonksiyon olarak overload resolution işlemine sokulur. Daha üst isim alanlarındaki fonksiyonlar aday fonksiyon olarak seçilmezler. Yani overload işlemi aynı isim alanındaki - aynı isimli fonksiyonların bulunması anlamına gelmektedir. Örneğin: + aynı isimli fonksiyonların bir arada bulunması anlamına gelmektedir. Örneğin: void foo(double a) { @@ -6772,7 +6809,7 @@ int main() std::cout << a << std::endl; - Biz şimdiye kadar böyle bir niteliklendirme yapmadık. Bunun nedeni programın yukarısa using namespace denilen direktifi yerleştirmiş olmamızdır: + Biz şimdiye kadar böyle bir niteliklendirme yapmadık. Bunun nedeni programın yukarısına using namespace denilen direktifi yerleştirmiş olmamızdır. using namespace std; @@ -6903,11 +6940,11 @@ int main() diğerlerinden farklı olacak bir isim ile kombine ederek amaç dosyaya yazmaktadır. İşte isimsiz isim alanları "modüle özgü global değişken oluşturabilmek" için kullanılmaktadır. Farklı modüllerdeki isimsiz isim alanları içerisinde aynı isimli değişkenler bulunabilir. Anımsanacağı gibi modüle özgü global değişkenler C'de "static" anahtar sözcüğü ile "static global" değişkenler olarak oluşturulmaktadır. C++'ta her ne kadar static global değişken bildirimi yine varsa da - bu yöntem "modüle özgü global değişken oluşturabilmek için" kötü bir teknik olarak kabul edilmektedir. static global değişkenler yerine C++'ta "isimsiz isim - alanları (unnamed namespaces)" kullanılmalıdır. Tabii derleyiciler isimsiz isim lanalarının içerisindeki değişkenleri aslında amaç dosyaya PUBLIC olarak - yazmamaktadır. Yani tıpkı static global değişkenlerde olduğu gibi bu değişkenler gerçekten ismi biliniyor olsa bile başka bir modülden extern bildirimi ile + bu yöntem "modüle özgü global değişken oluşturmak için kötü bir teknik"" olarak kabul edilmektedir. static global değişkenler yerine C++'ta "isimsiz isim + alanları (unnamed namespaces)" kullanılmalıdır. Tabii derleyiciler isimsiz isim alanlarının içerisindeki değişkenleri aslında amaç dosyaya PUBLIC olarak + yazmazlar. Yani tıpkı static global değişkenlerde olduğu gibi bu değişkenler gerçekten ismi biliniyor olsa bile başka bir modülden extern bildirimi ile kullanılamamaktadır. Başka bir deyişle aslında isimsiz isim alanınındaki isimlerin static belirleyicisi ile oluşturulmuş isimlerden link işlemi bakımından - bir farklılık oluşmamaktadır. + bir farkı yoktur. İsimsiz isim alanları başka isim alanlarının içerisinde de olabilmektedir. Örneğin: @@ -6983,7 +7020,7 @@ int main() } } - Biz burada G-x ve foo isimlerini inline CSD isim alanı içerisinde bildirdik. CSD isim alanı global isim alanı içerisinde olduğu için sanki bu isimler + Biz burada g_xx ve foo isimlerini inline CSD isim alanı içerisinde bildirdik. CSD isim alanı global isim alanı içerisinde olduğu için sanki bu isimler global isim alanı içerisindeymiş gibi de kullanılabilmektedir. Örneğin: g_x = 10; // geçerli, niteliklebdirmeye gerek yok @@ -6993,8 +7030,7 @@ int main() foo(); // geçerli, niteliklendirmeye gerek yok Tabii istersek yine bu isim alanındaki isimleri nitelikli de kullanbiliriz: - - + CSD::g_x = 10; // geçerli, açıkça niteliklendirme yapılmış cout << CSD::g_x << endl; // geçerli, açıkça niteliklendirme yapılmış @@ -7089,12 +7125,12 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 42) Gereksiz niteliklendirmeyi elimine edebilmek için "using namespace direktifi" denilen bir direktiften faydalanılmaktadır. using namespace direktifinin + 43) Gereksiz niteliklendirmeyi elimine edebilmek için "using namespace direktifi" denilen bir direktiften faydalanılmaktadır. using namespace direktifinin genel biçimi şöyledir: using namespace ; - Burada isim alanı ismi tek bir isimden olaşabileceği gibi :: operatörü kullanılarak oluşturulan iç bir isim alanı da olabilir. Örneğin: + Burada isim alanı ismi tek bir isimden olaşabileceği gibi :: operatörü kullanılarak oluşturulan niteliklendirilmiş bir isim alanı ismi de olabilir. Örneğin: using namespace CSD; using namespace CSD::Util::Test @@ -7106,7 +7142,7 @@ int main() Bu direktiflerin aşağıdaki gibi ayrı ayrı oluşturulması gerekir: - using namespace CSD; + using namespace CSD; using namespace CSD::Util; using namespace std; @@ -7114,11 +7150,11 @@ int main() belirtilen isim alanının daha önce derleyici tarafından görülmüş olması gerekmektedir. Yani önce isim alanı bildirimi yapılmalı daha sonra o isim alanı için using namespace direktifi kullanılmalıdır. Olmayan bir isim alanına using namespace direktifi uygulanamaz. - using namespace direktifini ele almadan önce "isim araması (namelookup)" denilen bir kavramdan kısaca bahsedeceğiz. İsim araması kavramı C standratlarınd olmayan - C++'ın karmaşıklığı nedeniyle C++ standartlarına bulunan bir kavramdır. Derleyici bir isim ile karşılaştığında o isme ilişkin bir bildirimi bulmaya çalışır. + using namespace direktifini ele almadan önce "isim araması (namelookup)" denilen bir kavramdan kısaca bahsedeceğiz. İsim araması kavramı C standratlarında olmayan + C++'ın karmaşıklığı nedeniyle C++ standartlarında bulunan bir kavramdır. Derleyici bir isim ile karşılaştığında o isme ilişkin bir bildirimi bulmaya çalışır. Çünkü her ismin bir bildirimi olmak zorundadır. İşte derleyici isimleri sırasıyla bazı faaliyet alanlarında aramaktadır. İsim bir faaliyet alanında bulunursa başka bir faaliyet alanına bakılmamaktadır. Yani arama isim bulunamadığı sürece devam ettirilmektedir. Örneğin C'de (her ne kadar bu kavram C'de yoksa da) - yerel bir blokte bir isim kullanıldığında önce o ismin bildirimi içten dışa doğru yerel bloklarda aranmaktadır. Nihayet bu yerel bloklarda bulunamazsa global + yerel bir blokta bir isim kullanıldığında önce o ismin bildirimi içten dışa doğru yerel bloklarda aranmaktadır. Nihayet bu yerel bloklarda bulunamazsa global alana da bakılmaktadır. C++'ta isim araması iki grubua ayrılmaktadır: 1) Niteliksiz isim araması (unqualified name lookup) @@ -7198,9 +7234,8 @@ int main() Burada direktifin yerleştirildiği isim alanı A, direktifte belirtilen isim alanı A::B::C'dir. İki isim alanını kapsayan en dar isim alanı A'dır. - - using direktif şöyle etki göstermektedir: Niteliksiz isim araması (unqualified name lokkup) sırasında sanki direktifte belirtilen isim alanının - içerisindekiler direktifin yerleştirildiği ve direktifte belirtilen isim alanı kapsana en dar isim alanına enjekte edilmiş gibi olmaktadır. Ancak + using direktifi şöyle etki göstermektedir: Niteliksiz isim araması (unqualified name lokkup) sırasında sanki direktifte belirtilen isim alanının + içerisindekiler direktifin yerleştirildiği ve direktifte belirtilen isim alanını kapsayan en dar isim alanına enjekte edilmiş gibi olmaktadır. Ancak bu enjekte edilme niteliksiz isim aramaları (unqualified name lookup) sırasında (yani doğrudan yazılan isimlerin aranması sırasında) ve yalnızca direktifin yerleştirildiği faaliyet alanında etkili olmaktadır. Örneğin: @@ -7391,7 +7426,6 @@ int main() Niteliksiz isim aramsı sırasında using namespace direkifi ile bleirtilen isim alanında da using namespace direktifi varsa etki geçişli olarak devam eder. Örneğin: - namespace A { int x; @@ -7459,7 +7493,6 @@ int main() } Burada tüm foo fonksiyonları aslında global isim alanındaymış gibi düşünülmelidir. Dolayısıyla bu fonksiyonların hepsi aday fonksiyon olarak seçilecektir. - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -7567,7 +7600,7 @@ int main() aranacaktır. Dolayısıyla isim B isim alanında bulunduğu için sorun çıkmayacaktır. Aşağıdaki örnekte ise C::x ifadesindeki x birden fazla isim alanında buulunacağı için isim araması başarısız olacaktır. Bu tür durumlarda using namespace - direkt,flerinin sırasının hiçbir önemi yoktur: + direktiflerinin sırasının hiçbir önemi yoktur: namespace A { @@ -7645,7 +7678,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 43) using namespace direktifinin dışında C++'ta ayrıca bir de "using bildirimi" denilen bir bildirim de vardır. using bildirimi bir isim alanındaki ismi + 44) using namespace direktifinin dışında C++'ta ayrıca bir de "using bildirimi" denilen bir bildirim de vardır. using bildirimi bir isim alanındaki ismi bir faaliyet alanına sokmak için kullanılmaktadır. Bu nedenle bu bir direktif değil bildirimdir. Çünkü bildirimlerde yeni bir isim bir faaliyet alanına katılmaktadır. using bildiriminin genel biçimi şöyledir: @@ -7675,7 +7708,7 @@ int main() //... } - using bildirimi bir fonksiyona uygulandığında o isim alanındaki aynı isimli tüm fonksiyonlar ilgili faaliyet alanına sokulmuş olur. Yalnızca aynı + using bildirimi bir fonksiyona uygulandığında o isim alanındaki aynı isimli tüm fonksiyonlar ilgili faaliyet alanına sokulmuş olur. Yalnızca aynı isimli tek bir fonksiyonunun using bildirimi ile faaliyet alanına sokulması mümkün değildir. Örneğin: namespace CSD @@ -7700,8 +7733,6 @@ int main() foo(10); // geçerli return 0; - } - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -7749,7 +7780,7 @@ int main() } /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 44) İsim alanlarına ilişkin diğer bir bildirim de "namespace alias" denilen bildirimdir. Bu bildirim bir isim alanı ismini daha kolay kullanmak için düşünülmüştür. + 45) İsim alanlarına ilişkin diğer bir bildirim de "namespace alias" denilen bildirimdir. Bu bildirim bir isim alanı ismini daha kolay kullanmak için düşünülmüştür. Örneğin isim alanı ismi uzun olabilir. Biz bunu kısa bir biçimde kullanmak isteyebiliriz. Ya da örneğin iç bir isim alanınını tek bir isim iel kullanmak isteyebiliriz. bildirimin genel biçimi şöyledir: @@ -7777,7 +7808,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 45) C++11 ile birlikte diğer bazı dillerde çeşitli biçimlerde bulunan "öznitelikler (attributes)" konusu dile eklenmiştir. Öznitelikler derleyici için + 46) C++11 ile birlikte diğer bazı dillerde çeşitli biçimlerde bulunan "öznitelikler (attributes)" konusu dile eklenmiştir. Öznitelikler derleyici için kullanılan direktiflerdir. Özniteliklerin temel amacı derleyicinin daha iyi kod üretmesini sağlamak, uyarı mekanizması üzerinde etkili olmak ve derleyicinin bazı davranışlarını değiştirmektir. @@ -7809,7 +7840,7 @@ int main() [[öznitelik_isim_alanı::öznitelik_ismi(argüman_listesi)]] [[using öznitelik_isim_alanı: öznitelik_ismi, öznitelik_ismi, ...]] - Yukarıdaki iki köşeli parantezler içerisindeki çznitelik isimleri birden fazla olabilir. Bu durumda öznitelikler ',' atomu ile ayrılmalıdır. Aşağıda + Yukarıdaki iki köşeli parantezler içerisindeki öznitelik isimleri birden fazla olabilir. Bu durumda öznitelikler ',' atomu ile ayrılmalıdır. Aşağıda bazı geçerli öznitelik oluşturma örnekleri vermek istiyoruz: [[xxx]] @@ -7901,7 +7932,7 @@ int main() //... } - - Parametre değişkenlerinde tür belirleyicisndne önce. Örneğin: + - Parametre değişkenlerinde tür belirleyicisinden önce. Örneğin: void foo([[xxx::yyy] int a, int b) { @@ -7947,9 +7978,9 @@ int main() derleyicilerin farklı öznitelikleri olabilmektedir. Her öznitelik her sentaktik öğede geçerli olmayabilir. Örneğin bazı öznitelikler yalnızca fonksiyon bidiriminde ya da tanımlamasında kullanılabilir. Bazı öznitelikler değişken tanımlamasında kullanılabilir. C++ standartlarında her derleyicinin desteklemesi gereken az sayıda öznitelik baştan belirlenmiştir. (C++'ın çeşitli sürümlerinde bu listeye eklemeler yapılmıştır.) Bunlara "standart öznitelikler" diyebiliriz. - Standanrtlara göre isim alanı içermeyen tüm öznitelikler ve std isim alanı içeren öznitelikler "reserved" bırakılmıştır. Yani bunların programcılar tarafından + Standandartlara göre isim alanı içermeyen tüm öznitelikler ve std isim alanı içeren öznitelikler "reserved" bırakılmıştır. Yani bunların programcılar tarafından ve derleyiciler tarafından kullanılması yasaklanmıştır. (Genel olarak standartlarda "reserved" özelliklerin kullanılması "tanımsız davranış" olarak ele - alınmaktadır.) Örneğin [[xxx]] biçiminde isim alanı içermeyen bir öznitelik programcılar tarafından da derleyicleri yazanlar tarafından da kullanılmamalıdır. + alınmaktadır.) Örneğin [[xxx]] biçiminde isim alanı içermeyen bir öznitelik programcılar tarafından da derleyicileri yazanlar tarafından da kullanılmamalıdır. Benzer biçimde [[std:xxx]] biçimindeki bir öznitelik de "reserved" durumdadır. O halde derleyicileri yazanlar kendileri öznitelik isim alanı uydurup kendi özniteliklerini bu öznitelik isim alanı ile oluşturmalıdırlar. Örneğin [[gnu::xxx]] gibi, [[msvc::xxx] gibi. Ayrıca standartlar "derleyici tarafından tanınmayan" bütün özniteliklerin derleyici tarafından "görmezden gelinmesi (ignore) gerektiğini" belirtmektedir. Bu durumda biz derleyicilerde olmayan bir öznitelik ismi @@ -7991,7 +8022,7 @@ int main() { //... - for (;;) { // sonsuz dönü + for (;;) { // sonsuz döngü //... } } @@ -8003,11 +8034,10 @@ int main() throw exception(); } - Öte yandan standrat kütüphanedeki bazı fonksiyonlar da artık [[noreturn]] ile bildirilmiştir. Örneğin exit fonksiyonu böyledir: + Öte yandan standart kütüphanedeki bazı fonksiyonlar da artık [[noreturn]] ile bildirilmiştir. Örneğin exit fonksiyonu böyledir: [[noreturn]] void exit(int exit_code); - Pekiyi bir fonksiyonun geri dönmeyeceğini derleyiciye söylemekle kim ne kazanmış olmaktadır? Derleyiciler fonksiyonları geri döndürebilmek için bazı makine komutlarını üretilen koda yerleştirmek zorundadır. (Örneğin pek çok işlemcide geri dönüşü "ret" isimli makine komutu sağlamaktadır. Ancak tek başına bu "ret") makine komutu yeterli de olmayabilir. Derleyici bazı yazmaçları geri dönmeden önce girişteki değerlerle yeniden yüklemek zorunda kalabilmektedir. Dolayısıyla @@ -8077,11 +8107,11 @@ int main() [[assume((h(), x == z))]]; h(); - g(x); // Derleyici bu işlemi g(3) ile eşdeğer olarak ele alabilir + g(x); // Derleyici bu işlemi g(3) ile eşdeğer olarak ele alabilir. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Standart [[fallthrough]] özniteliği swith deyimlerinde case etiketleri için kullanılmaktadır. Öznitelik boş deyimlere uygulanabilmektedir. Fallthroug işleminin + Standart [[fallthrough]] özniteliği swith deyimlerinde case etiketleri için kullanılmaktadır. Öznitelik boş deyimlere uygulanabilmektedir. Fallthrough işleminin kasten yapıldığını belirtmektedir. Dolayısıyla derleyiciler bu tür durumlarda "yanlışlıkla yapılan fallthrough işlemlerinde" verdikleri uyarı mesajlarını vermezler. Bu öznitelik C++17 ile birlikte standartlara eklenmiştir. Örneğin: @@ -8107,7 +8137,7 @@ int main() [[maybe_unused]] int a; - void foo(int a, [[maybe_unused]] b) + void foo(int a, int [[maybe_unused]] b) { //... } @@ -8115,7 +8145,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 46) C++'ın çeşitli versyonlarında deyimlerde de bazı eklemeler yapılmıştır. + 47) C++'ın çeşitli versyonlarında deyimlerde de bazı eklemeler yapılmıştır. C++17 ile birlikte if deyimine isteğe bağlı bir "init kısmı" eklenmiştir. Bu init kısmı bir ifade içerir. İfadeden sonra ';' atomunun bulunması gerekir. Yukarıda da belirttiğimiz gibi bu "init" kısmı deyidme bulundurulmak zorunda değildir. Dolayısıyla deyim eski biçimiyle uyumludur. Örneğin: @@ -8161,9 +8191,10 @@ int main() } C++20 ile birlikte Aralık tabanlı for döngülerine de "init" kısmının eklendiğini aralık tabanlı for döngülerini anlattığımız kısımda belirtmiştik. +--------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 47) C++17 ile birlikte "constexpr if" C++23 ile de "consteval if" deyimi biçiminde yeni iki if deyimi varyasyonu eklenmiştir. constexpr if deyiminin genel biçimi + 48) C++17 ile birlikte "constexpr if" C++23 ile de "consteval if" deyimi biçiminde yeni iki if deyimi varyasyonu eklenmiştir. constexpr if deyiminin genel biçimi şöyledir: if constexpr ([init;] koşul) @@ -8244,14 +8275,14 @@ int main() Burada cube fonksiyonu parametresi ile aldığı sayının kübüyle geri dönmektedir. Ancak bu constexpr fonksiyon sabit ifadesi gereken bir bağlamda da çağrılmış olabilir, sabit ifadesi gerekmeyen bir bağlamda da çağrılmış olabilir. Eğer bu fonksiyon sabit ifadesi gereken bir bağlamda çağrılmışsa bu durumda consteval if - deyimim doğruysa kısmı derleme aşamasında devreye girecek ve a * a * a işlemi ile derleme aşamsında değer hesaplanacaktır. Eğer bu fonksiyon sabit ifadesi gerekmeyen - bir bağlamda çağrılmışsa bu durumda consteval if deyiminin yanlışsa kısmı devreye girecek ve küp alma işlemi derleme zanında değil fonksiyon çağrılarak + deyimim doğruysa kısmı derleme aşamasında devreye girecek ve a * a * a işlemi ile derleme aşamsında değer hesaplanacaktır. Eğer bu fonksiyon sabit ifadesi + gerekmeyen bir bağlamda çağrılmışsa bu durumda consteval if deyiminin yanlışsa kısmı devreye girecek ve küp alma işlemi derleme zanında değil fonksiyon çağrılarak yapılacaktır. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 48) C++'ta ilk standartlardan beri "anonim birlik (anonymous union)" oluşturma özelliği bulunmaktadır. Anonim birlik oluşturabilmek için birliğe bir isim - verilmemesi ve birlik bildiriminde aynı zamanda nesne tanımlamanın yapılmamış olamsı gerekmektedir. Örneğin: + 49) C++'ta ilk standartlardan beri "anonim birlik (anonymous union)" oluşturma özelliği bulunmaktadır. Anonim birlik oluşturabilmek için birliğe bir isim + verilmemesi ve birlik bildiriminde aynı zamanda nesne tanımlamanın yapılmamış olması gerekmektedir. Örneğin: union { int a; @@ -8320,10 +8351,10 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 49) C++11 ile birlikte dile static_assert isimli bir bildirim (decalartion) eklenmiştir. static_assert bildirimi derleme zamanında assert kontrolü yapmaktadır. - Bilindiği gibi standart assert fonksiyonu programın çalışma zamanı sırasında işleme sokulmkatadır. Halbuki static_assert bildirimi derleme aşamsında işleme + 50) C++11 ile birlikte dile static_assert isimli bir bildirim (decalartion) eklenmiştir. static_assert bildirimi derleme zamanında assert kontrolü yapmaktadır. + Bilindiği gibi standart assert fonksiyonu programın çalışma zamanı sırasında işleme sokulmkatadır. Halbuki static_assert bildirimi derleme aşamasında işleme sokulur. Derleme aşamasında #if önişlemci komutuyla da bazı kontroller yapılıp #error önişlemci komutuyla derleme işlemi sonlandırılabilmektedir. - Ancak önişlemci aşamasında yapılacek kontroller oldukça sınırlıdır. assert bildiriminin genel biçimi şöyedidr: + Ancak önişlemci aşamasında yapılacek kontroller oldukça sınırlıdır. static_assert bildiriminin genel biçimi şöyedidr: static_assert(sabit_ifadesi_koşulu, "mesaj"); @@ -8350,7 +8381,7 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - 50) C99 ile birlikte C'ye "designated initializer" denilen ilkdeğer verme biçimi eklenmişti. Bu sayede artık C99 ve sonrasında yapıların ve dizilerin + 51) C99 ile birlikte C'ye "designated initializer" denilen ilkdeğer verme biçimi eklenmişti. Bu sayede artık C99 ve sonrasında yapıların ve dizilerin elemanlarına sırasıyla ilkdeğer verme zorunluğu kaldırılmış oldu. C'de sentaks aşağıdaki gibidir: struct SAMPLE { @@ -8365,7 +8396,7 @@ int main() - C++'ta dizilerde designated initializer kullanımı yoktur. - Bazı koşulları sağlayan C ile uyumlu yapılarda ve sınıflarda (C++'ta yapılar da birer sınıftır) designated initializer kullanılabilmektedir. Ancak yapıların - ve sınıfların sıralı elemanlarına bu biçimde değer verilebilmektedir. + ve sınıfların sıralı elemanlarına bu biçimde değer verilebilmektedir. (Yani ilkdeğer verilen elemanların sırası yapı bildirimindeki sıraya uygun olmalıdır.) struct Sample { int a, b, c, d; @@ -8460,7 +8491,6 @@ int main() // buradaki elemanlar private bölümde public: // buradaki elemanlar public bölümde - protected: // buradaki elemanlar protected bölümde }; @@ -8535,7 +8565,7 @@ int main() //... } - Standratlara göre üye fonksiyon tanımalamaları sınıf bildirimini kapsayan daha dış bir isim alanında isim alanını ismi de bedlirtilerek yapılabilir. Örneğin: + Standartlara göre üye fonksiyon tanımalamaları sınıf bildirimini kapsayan daha dış bir isim alanında isim alanını ismi de belirtilerek yapılabilir. Örneğin: namespace CSD { @@ -8664,7 +8694,7 @@ int main() //... } - Java ve C# gibi dillerde prototip olmadığı için mecburen sınıfın metotları sınıf içerisinde tanılanmaktadır. Bu dillerde inline diye bir kavram da yoktur. + Java ve C# gibi dillerde prototip olmadığı için mecburen sınıfın metotları sınıf içerisinde tanımlanmaktadır. Bu dillerde inline diye bir kavram da yoktur. Bu dillerde metotlar sınıfın içerisinde yazılmak zorunda olduğu için bölüm C++'taki gibi bölüm kavramının okunabilirliği azaltacağı düşünülmüştür. Bu nedenle bu dillerde bölüm belirten anahtar sözcükler ayrıca her alan'da ve metotta belirtilmektedir. Örneğin: @@ -8731,7 +8761,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- C++'ta sınıflar (yapıların ve birliklerin de bir sınıf olduğunu anımsayınız) bir "faaliyet alanı (scaope)" da belirtmektedir. Dolayısıyla C++'ta farklı - sınıflarda aynı isimli ve aynı parametrik yapıya sahip fonksiyonlar bulunabilir. Bunlar farklı faaliyet alanlarında bulundurklarından sorun oluşturmazlar. + sınıflarda aynı isimli ve aynı parametrik yapıya sahip fonksiyonlar bulunabilir. Bunlar farklı faaliyet alanlarında bulunduklarından sorun oluşturmazlar. Tabii aynı zamanda aynı isimli ve aynı parametrik yapıya sahip bir global fonksiyon da bulunabilmektedir. Örneğin: class Sample { @@ -8820,9 +8850,9 @@ int main() static void tar(); // nesne içerisinde yer kaplamaz! static üye fonksiyonlar ileride ele alınacaktır. }; - Pekiyi bir sınıf nesnesi içerisindeki eleman organizasyonu nasıldır? İşte sınıf türünden türünden nesneler statik olmayan veri elemanlarından oluşan bileşik + Pekiyi bir sınıf nesnesi içerisindeki eleman organizasyonu nasıldır? İşte sınıf türünden nesneler statik olmayan veri elemanlarından oluşan bileşik nesnelerdir. C++ standartlarına göre "iki bölüm belirten anahtar sözcük arasındaki veri elemanları ilk yazılan eleman düşük adreste olacak biçimde ardışıl - bir biçimde" yerleştirilir. Ancak farklı bölümlerdeki elemanların birbirlerine göre durumu standartlarda derleyicileri yazanların isteğine bırakılmıştır. + olarak" yerleştirilir. Ancak farklı bölümlerdeki elemanların birbirlerine göre durumu standartlarda derleyicileri yazanların isteğine bırakılmıştır. Fakat yaygın tüm derleyiciler bölüm farkı gözetmeksizin ilk yazılan eleman düşük adreste olacak biçimde ardışıl bir yerleşim uygulmaktadır. Tabii yine derleyiciler C'de olduğu gibi elemanlar arasında hizalama amaçlı kontrollü boşluklar (padding) bırakabilmektedir. Örneğin: @@ -9182,23 +9212,23 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Pekiyi bu durumda sınıfın veri elemanları ve üye fonksiyonları ne anlama gelmektedir? İşte sınıfın veri elemanları aslında üye fonksiyonlar tarafından ortak bir biçimde kullanılan data'ları temsil etmektedir. Sınıfın üye fonksiyonları aynı nesnenin veri elemanlarını ortak olarak kullanmaktadır. Bir üye - fonksiyon o elemanlara değer yerleştirdiğinde diğer bir üye fonksiyon o değerleri kullanabilmektedir. Sınıflar belli bir konuda faydalı işlemler yapan - üye fonksiyonlardan oluşmaktadır. Üye fonksiyonların da bu faydalı işlemleri yapabilmesi için ortak veri elemanlarını kullanması gerekmektedir. + fonksiyon o elemanlara değer yerleştirdiğinde diğer bir üye fonksiyon o değerleri kullanabilir. Sınıflar belli bir konuda faydalı işlemler yapan + üye fonksiyonlardan oluşmaktadır. Üye fonksiyonlar da bu faydalı işlemleri ortak veri elemanlarını kullanarak gerçekleştirmektedir. NYPT'de sınıflar belli bir konu (kavram) üzerinde işlemler yapan üye fonksiyonlar gibi değerlendirilebilir. Örneğin Date isimli bir sınıf tarih konusunda - yararlı birtakım işlemleri yapan üye fonksiyonlara sahip olabilir. String isimli bir sınıf bir yazı üzerinde işelmler yapan üye fonksiyonlara sahip olabilir. - Benzer biçimde SerialPort isimli bir sınıf seri port işlemlerini yapan üye fonksiynlara sahip olabilir. + yararlı birtakım işlemleri yapan üye fonksiyonlara sahip olabilir. String isimli bir sınıf bir yazı üzerinde işelemler yapan üye fonksiyonlara sahip olabilir. + Benzer biçimde SerialPort isimli bir sınıf seri port işlemlerini yapan üye fonksiyonlara sahip olabilir. NYPT'de artık fonksiyonlar temelinde konuşmak yerine sınıflar temelinde konuşulur. Böylece "birbirinden farklı çok sayıda fonksiyon var" algısı yerine - "belli işlemleri yapan belli sınıflar var" oluşturulmaktadır. Bu da tasarım ve yazım işlemlerini algısal bakımdan kolaylaştırmaktadır. + "belli işlemleri yapan belli sınıflar var" algısı oluşturulmaktadır. Bu da tasarım ve yazım işlemlerini algısal bakımdan kolaylaştırmaktadır. - O halde biz NYPT'de bir sınıf ile karşılaştığımıuzda "bu sınıfın belli bir konu üzerinde faydalı işlemler yapan üye fonksiyonlara sahip olduğunu, bu üye + O halde biz NYPT'de bir sınıf ile karşılaştığımızda "bu sınıfın belli bir konu üzerinde faydalı işlemler yapan üye fonksiyonlara sahip olduğunu, bu üye fonksiyonların da sınıfın veri elemanlarını (yani nesnesinin parçalarını) ortak biçimde kullanıdığını" anlamalıyız. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Sınıfın bir üye fonksiyonu başka bir üye fonksiyonunu doğrudan (niteliklendirmeden) çağırabilir. Çünkü üye fonksiyonlar da sınıf faaliyet alanındadır. Bu durumda - çağıran üye fonksiyon hangi nesneyle çağrılmışsa çağrılan üye fonksiyonun da aynı nesneyle çağrılmış olduğu kabul edilir. Örneğin: + Sınıfın bir üye fonksiyonu başka bir üye fonksiyonunu doğrudan (niteliklendirmeden) çağırabilir. Çünkü üye fonksiyonlar da sınıf faaliyet alanındadır. Bu + durumda çağıran üye fonksiyon hangi nesneyle çağrılmışsa çağrılan üye fonksiyonun da aynı nesneyle çağrılmış olduğu kabul edilir. Örneğin: class Sample { public: @@ -9344,7 +9374,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Bir sınıf nesnesi yaratıldığında derleyici tarafından otomatik olarak çağrılan sınıfın özel üye fonksiyonlarına "yapıcı fonksiyonlar (constructors)" denilmektedir. - Yapıcı fonksiyonlar tipik olarak birtakım ilk işlemleri yapmak ve sınıfın veri elemanlarına birtakım ildeğerleri vermek için kullanılmaktadır. + Yapıcı fonksiyonlar tipik olarak birtakım ilk işlemleri yapmak ve sınıfın veri elemanlarına birtakım ilkdeğerleri vermek için kullanılmaktadır. Yapıcı fonksiyonlar sınıf ismiyle aynı isimli olan üye fonksiyonladır. Örneğin @@ -9366,7 +9396,7 @@ int main() } Yapıcı fonksiyonların geri dönüş değerleri diye kavramları yoktur. Bu nedenle yapıcı fonksiyonlarda geri dönüş değerlerinin türü yerine hiçbir şey yazılmaz. - Geri dönüş değerinin türü yerine void yazmak da geçerli bir durum değildir. Çünkü "void" ger i dönüş değerinin olmadığı anlamına gelmektedir. Halbuki yapıcı + Geri dönüş değerinin türü yerine void yazmak da geçerli bir durum değildir. Çünkü "void" geri dönüş değerinin olmadığı anlamına gelmektedir. Halbuki yapıcı fonksiyonların böyle bir kavramları yoktur. Yapıcı fonksiyonlar içerisinde return deyimini kullanabiliriz. Ancak onun yanına bir ifade yazamayız. @@ -9386,7 +9416,7 @@ int main() Burada yaratılan SerialPort nesnesi için yapıcı fonksiyon çağrılacaktır. Bu fonksiyon da seri port işlemleri için gerekli birtakım ilk işlemleri yapabilecektir. - Yapıcı fonksiyon içerisinde kullanolan sınıfın veri elemanarı o anda yaratılmış olan nesnenin veri elemanlarıdır. Örneğin: + Yapıcı fonksiyon içerisinde kullanılan sınıfın veri elemanarı o anda yaratılmış olan nesnenin veri elemanlarıdır. Örneğin: class Sample { public: @@ -9414,7 +9444,6 @@ int main() s.disp() Burada s nesnesi yaratıldığında yapıcı fonksiyon çağrılacaktır. Onun içerisindeki a ve b elemanları s nesnesinin veri elemanıdır. - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -9451,7 +9480,7 @@ int main() } /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - Sınıfın yapıcı fonksiyonları overload edilebilir. Yani sınıfta birden fazla yapıcı fonksiyon bulunabilir. Örneğin: + Sınıfın yapıcı fonksiyonları overload edilebilir. Yani sınıfta farklı parametrik yapılara ilişkin birden fazla yapıcı fonksiyon bulunabilir. Örneğin: class Sample { public: @@ -9506,7 +9535,7 @@ int main() Bu durumda sınıfın bütün yapıcı fonksiyonları aday fonksiyonlardır. Bunlar arasından uygun olanlar seçilir ve uygun olanlar arasından da en uygun yapıcı fonksiyon seçilmeye çalışılır. - C++11 ile birlikte "uniform initializer" sentaks dile eklendikten sonra nesne yaratırken normal parantezler yerine kğme parantezleri de kullanılabilmektedir. + C++11 ile birlikte "uniform initializer" sentaks dile eklendikten sonra nesne yaratırken normal parantezler yerine artık küme parantezleri de kullanılabilmektedir. Örneğin: Sample a{10, 20}; // C++11 ve sonrasında geçerli @@ -9600,8 +9629,8 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Global sınıf nesneleri için yapıcı fonksiyonlar akış henüz main fonksiyonuna girmeden çağrılmaktadır. Çağrılma sırası kaynak dosyadaki (translation unit) - yuakarıdan aşağıya ve soldan sağa yönüne göredir. Yani kaynak kodda daha önce tanımlanan nesnenin yapıcı fonksiyonu daha sonra tanımlanandna önce çağrılır. - Ancak proje birden fazla kaynak dosyadan oluşyorsa kaynak dosyalardaki global nesneler birbirilerine göre yapıcı fonksiyon çağrılma sırası standartlarda + yuakarıdan aşağıya ve soldan sağa yönüne göredir. Yani kaynak kodda daha önce tanımlanan nesnenin yapıcı fonksiyonu daha sonra tanımlanandan önce çağrılır. + Ancak proje birden fazla kaynak dosyadan oluşyorsa kaynak dosyalardaki global nesnelerin birbirilerine göre yapıcı fonksiyon çağrılma sırası standartlarda "belirsiz (unspecified)" bırakılmıştır. Örneğin: // a.cpp @@ -9615,7 +9644,7 @@ int main() Sample g_y; Burada g_a nesnesi için yapıcı fonksiyon g_b nesnesi için yapıcı fonksiyondan daha önce çağrılır. Benzer biçimde g_x için yapıcı fonksiyon g_y için yapıcı - fonksiyondan daha önce çağrılacaktır. Ancak kaynak doslayar arasındaki sıra konusunda bir belirleme yapılmamıştır. + fonksiyondan daha önce çağrılacaktır. Ancak kaynak dosyalar arasındaki sıra konusunda bir belirleme yapılmamıştır. Aşağıdaki örneği çalıştırarak durumu inceleyiniz. Ekranda şunları göreceksiniz: @@ -9628,7 +9657,6 @@ int main() main continues... Sample(int): 30 main ends... - --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #include @@ -9670,7 +9698,7 @@ Sample z{300}; Pekiyi static yerel sınıf nesneleri için yapıcı fonksiyonlar ne zaman çağrılmaktadır? İşte static yerel sınıf nesneleri için yapıcı fonksiyonlar "programın akışı nesnenin tanımlandığı noktaya ilk kez geldiğinde yalnızca bir kez" çağrılmaktadır. Bu cümleden iki sonuç çıkmaktadır: - 1) Eğer akış static yerel sınıf nesnesinin tanımlandığı noktaya hiç gelmezse onun yapıcı fonksiyon hiç çağrılmayacaktır. + 1) Eğer akış static yerel sınıf nesnesinin tanımlandığı noktaya hiç gelmezse onun için yapıcı fonksiyon hiç çağrılmayacaktır. 2) Akış static yerel sınıf nesnelerinin tanımlandığı noktaya birden fazla kez gelirse yalnızca ilk gelişinde nesne için yapıcı fonksiyon çağrılacaktır. Aşağıdaki örneği çalıştırarak sonucu inceleyiniz. Ekrana şunları göreceksiniz: @@ -9766,7 +9794,7 @@ Sample z{300}; class Sample { public: - Sample (int a); + Sample(int a); //... }; //... @@ -9838,7 +9866,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- C++11 ile birlikte "açıkça default hale getirilmiş default yapıcı fonksiyon (explicitly defaulted default constructor)" biçiminde bir kavram da dile eklenmiştir. - Bir default yapıcı fonksiyon için prototipten sonra " = default" sentaksı kullanılırsa bu sentaks "default yapıcı fonksiyonu içi boş olarak derleyici tarafından + Bir default yapıcı fonksiyon için prototipten sonra " = default" sentaksı kullanılırsa bu sentaks "default yapıcı fonksiyon içi boş olarak derleyici tarafından yazılsın" anlamına gelmektedir. Örneğin: class Sample { @@ -9876,8 +9904,8 @@ int main() //... }; - Burada sınıf için hiçbir yapıcı fonksiyon yazılmadığı için derleyici defauşt yapıcı fonksiyonu içi boş olarak yazacaktır. Ancak biz default yapıcı fonksiyonu - açıkça silinmiş hale getirirsek derleyici artık default yapıcı fonksiyonu biizm yazmaz. Örneğin: + Burada sınıf için hiçbir yapıcı fonksiyon yazılmadığı için derleyici default yapıcı fonksiyonu içi boş olarak yazacaktır. Ancak biz default yapıcı fonksiyonu + açıkça silinmiş hale getirirsek derleyici artık default yapıcı fonksiyonu bizim yazmayacaktır. Örneğin: class Sample { public: @@ -9938,6 +9966,7 @@ int main() class Sample { public: int a, b; + void foo(); void bar(); }; @@ -9945,10 +9974,10 @@ int main() Sample s; // s'in a ve b elemanlarında hangi değerler vardır? - Bu konu ileride MIL sentaksı içerisinde haada detaylı bir biçimde açıklanacaktır. Ancak burada pratik bazı şeyler söylmek istiyoruz. Eğer sınıfın yapıcı + Bu konu ileride MIL sentaksı içerisinde daha detaylı bir biçimde açıklanacaktır. Ancak burada pratik bazı şeyler söylmek istiyoruz. Eğer sınıfın yapıcı fonksiyonlarında veri elemanlarına değer atanmamışsa kabaca (ayrıntılar ileride ele alınacak) şu durumlar söz konusudur: - 1) Eğer sınıf nesnesi yerel ise sınıfın temel türlere ilişkin veri elemanlarına herhangi bir değer atanmaz. Dolayısıyla o elemanlarda çöp değer bulunur. + 1) Eğer sınıf nesnesi yerel ise sınıfın temel türlere ilişkin veri elemanlarına herhangi bir değer atanmaz. Dolayısıyla o elemanlarda çöp değerler bulunur. Yani yukarıdaki örnekte s nesnesi yerel ise tanımlama sonrasında a ve b veri elemanlarında çöp değerler (indeterminate values) bulunacaktır. (Buna standartlarda "default-initilize" denilmektedir: 9.4.1-6) @@ -9959,8 +9988,8 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- Standartlarda "kullanıcı tarafından yazılmış olan default yapıcı fonksiyon (user-provided default constructor)" terimi default yapıcı fonksiyonun programcı tarafından açıkça yazıldığını belirtmektedir. Açıkça default hale getirilmiş (explicitly defaulted) ve silinmiş (deleted) default yapıcı fonksiyonlar - "user-provided" yapıcı fonksiyon" değillerdir. Bir sınıfta hiçbir yapıcı fonksiyon yoksa derleyicinin yazdığı default yapıcı - fonksiyon da "user-provided" değildir. + "user-provided" yapıcı fonksiyonlar" değildir. Bir sınıfta hiçbir yapıcı fonksiyon yoksa derleyicinin yazdığı default yapıcı fonksiyon da "user-provided" + değildir. --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -10057,7 +10086,7 @@ int main() /*------------------------------------------------------------------------------------------------------------------------------------------------------------- C++11 ile birlikte "default member initializer" ismiyle sınıfın veri elemanlarına sınıf bildirimi içerisinde ilkdeğer verme sentaksı da dile eklenmiştir. - Aslında sınıfın veri elemanalrına sınıf bildirimi içerisinde ilkdeğer verme zaten Java ve C# gibi dillerde çok önceden beri bulunyordu. C++ bu özelliği + Aslında sınıfın veri elemanalarına sınıf bildirimi içerisinde ilkdeğer verme zaten Java ve C# gibi dillerde çok önceden beri bulunuyordu. C++ bu özelliği C++11 ile bünyesine katmıştır. Bu özelliğe göre biz sınıfın veri elemanalrına yapıcı fonksiyonun yanı sıra istersek artık sınıf bildirimi içerisinde de ilkdeğer verebiliriz. Örneğin: @@ -10069,14 +10098,14 @@ int main() double imag = 0; }; - Anımsanacağı gibi C'de yapılar için böyle bir sentaks yoktur. Pekiyi bu sentaks ne anlama gelmektedir? Aslında derleyiciler bu durumda veri elemanına verilen - ilkdeğerlerin hepsini bildirim sırasına göre atama deyimlerine dönüştürerek sınıfın tüm yapıcı fonksiyonlarının ana bloğunun başına gizli bir biçimde - yerleştirmektedir. Yani bu ilkdeğerler aslında sınıfın tüm yapıcı fonksiyonlarında ilgili veri elemanına verilmiş gibi bir işlem söz konusu olmaktadır. - Standartlar bir veri elemanına hem sınıf bildirimi içerisinde hem de yapıcı fonksiyonda ilkdeğer verildiğinde sınıf bildirimind everilen ilkdeğerin dikkate + Anımsanacağı gibi C'de yapılar için böyle bir sentaks yoktur. Pekiyi bu sentaks ne anlama gelmektedir? Aslında derleyiciler bu durumda veri elemanlarına + verilen ilkdeğerlerin hepsini bildirim sırasına göre atama deyimlerine dönüştürerek sınıfın tüm yapıcı fonksiyonlarının ana bloğunun başına gizli bir biçimde + yerleştirmektedir. Yani bu ilkdeğerler aslında sınıfın tüm yapıcı fonksiyonlarında ilgili veri elemanlarına atanmış gibi bir durum söz konusu olmaktadır. + Standartlar bir veri elemanına hem sınıf bildirimi içerisinde hem de yapıcı fonksiyonda ilkdeğer verildiğinde sınıf bildiriminde verilen ilkdeğerlerin dikkate alınmayacağını (ignore edileceğini) belirtmektedir. Bu anlatım verilen ilkdeğerilerin atama deyimlerine dönüştürülerek tüm yapıcı fonksiyonların başına yerleştirilmesi anlatımı ile aynı anlama gelmektedir. - Pekiyi eskiden olmayan bu zöelliğin C++11 ile eklenmesinin ne amacı vardır? İşte sınıfın çok veri elemanı ve çok yapıcı fonksiyonu olabilir. Bazı veri + Pekiyi eskiden olmayan bu özelliğin C++11 ile eklenmesinin ne amacı vardır? İşte sınıfın çok veri elemanı ve çok yapıcı fonksiyonu olabilir. Bazı veri elemanlarına her yapıcı fonksiyonda aynı ilkdeğerler veriliyor olabilir. Bu özellik sayesinde programcı bu elemanlara tüm yapıcı fonksiyonalar içerisinde aynı değeri atamak yerine sınıf bildirimi içerisinde bir kez bu atamayı yapabilir. @@ -10193,8 +10222,8 @@ int main() --------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------------------------------------------------------- - C++'ta yapıcı fonksiyonların sınıfın veri elemanlarına ilkdeğerlerini vermesi için ismine "MIL Sentaksı (Member Initializer Syntax)" ya standart terminolojisi ile - "ctor-initializer" denilen alternatif bir sentaks da bulunmaktadır. MIL sentaksı başından beri C++'ta zaten vardı. MIL sentaksı bazı durumlarda alternatif + C++'ta yapıcı fonksiyonların sınıfın veri elemanlarına ilkdeğerlerini vermesi için ismine "MIL Sentaksı (Member Initializer Syntax)" ya standart terminolojisi + ile "ctor-initializer" denilen alternatif bir sentaks da bulunmaktadır. MIL sentaksı başından beri C++'ta zaten vardı. MIL sentaksı bazı durumlarda alternatif bir ilkdeğer verme biçimi olarak, bazı durumlarda da zorunlu bir ilkdeğer verme biçimi olarak kullanılmaktadır. İleride bu sentaksın semantiğine ilişkin ayrıntılı bilgiler vereceğiz. Burada yalnızca sentaksın yalın kullanımını ve anlamını açıklayacağız. @@ -10253,10 +10282,10 @@ int main() MIL sentaksı aslında sınıfın başka sınıf türünden veri elemanlarına ilkdeğer vermek için ve taban sınıfa ilkdeğer vermek için de kullanılmaktadır. Yani sentaksın semantiği biraz ayrıntılıdır. Biz burada geldiğimiz yere kadarki konuları dikkate alarak bir semantik açıklama yapacağız. - MIL sentaksındaki ilkdeğer ifadesinde kullanılan isimler aranması nasıl olmaktadır? Yani biz burada hangi değişkenleri kulanabiliriz? İşte bu ilkdeğer - ifadesinideki değişkenler sanki yapıcı fonksiyonun ana bloğunun hemen başında kullanılıyormuş gibi bir etki oluşturmaktadır. Yani isim araması sanki buradaki - isimler yapıcı fonksiyonun ana bloğunun başına kullanılmış gibi yapılmaktadır. Dolayısıyla biz ilkdeğer ifadesinde parametre değişkenlerini, global değişkenleri - ve sınıfın veri elemanlarını kullanabiliriz. Örneğin: + MIL sentaksındaki ilkdeğer ifadesinde (parantezin içerisindeki ifadelerde) kullanılan isimlerin aranması nasıl yapılmaktadır? Yani biz burada hangi değişkenleri + kulanabiliriz? İşte bu ilkdeğer ifadesinideki değişkenler sanki yapıcı fonksiyonun ana bloğunun hemen başında kullanılıyormuş gibi bir etki oluşturmaktadır. + Yani isim araması sanki buradaki isimler yapıcı fonksiyonun ana bloğunun başına kullanılmış gibi yapılmaktadır. Dolayısıyla biz ilkdeğer ifadesinde parametre + değişkenlerini, global değişkenleri ve sınıfın veri elemanlarını kullanabiliriz. Örneğin: class Sample { public: @@ -10307,7 +10336,7 @@ int main() sıraya göre değil bildirimdeki sıraya göre yapılmaktadır. Dolayısıyla burada a ve b değişkenlerine çöp değerler atanacaktır. C'de ve C++'ta genel olarak çöp değerleri kullanmak "tanımsız davranış (undefined behavior)" kabul edilmektedir. - Yukarıda da belirttğimiz gibi her zaman önce MIL sentaksındaki ilkdeğer verm işlemi bildirimdeki sıraya göre yapılmakta sonra yapıcı fonksiyonun ana bloğu + Yukarıda da belirttğimiz gibi her zaman önce MIL sentaksındaki ilkdeğer verme işlemi bildirimdeki sıraya göre yapılmakta sonra yapıcı fonksiyonun ana bloğu çalıştırılmaktadır. Bu durumda biz sınıfın bir veri elemanına hem MIL sentaksında hem de yapıcı fonksiyonun ana bloğunda değer atarsak ana blokta atadığımız değer nihai değer olarak veri elemanında kalacaktır. Örneğin: @@ -10318,7 +10347,7 @@ int main() Burada nesnenin a veri elemanında 100 bulunacaktır. - C++ derleyicileri tipik olarak MIL sentaksındaki ilkdeğer verme işlemlerini atama deyimlerine dönüştürerek gizlice yapıcı fonksiyonunana bloğunun başına + C++ derleyicileri tipik olarak ""MIL sentaksındaki ilkdeğer verme işlemlerini atama deyimlerine dönüştürerek"" gizlice yapıcı fonksiyonun ana bloğunun başına yerleştirmektedir. Böylece örneğin: Sample::Sample() : a(10), b(20), c(30) @@ -10371,7 +10400,7 @@ int main() C++11 ile birlikte sınıfın veri elemanlarına sınıf bildirimi içerisinde ilkdeğer verilebiliyordu (default member initializer). Bu ilkdeğerlerin de derleyiciler tarafından sınıfın yapıcı fonksiyonlarının başına taşındığından bahsetmiştik. Pekiyi hem bir veri elemanına sınıf bildirimi içerisinde ilkdeğer verilmişse hem de MIl sentaksında ilkdeğer verilmişse veri elemanında hangi değer bulunacaktır? C++ standartları bu durumda sınıf bildiriminde verilen ilkdeğerin dikkate alınmayacağını - yani MIL sentaksındaki değerin veri elemanında gözzükeceğini belirtmektedir. Örneğin: + yani MIL sentaksındaki değerin veri elemanında gözükeceğini belirtmektedir. Örneğin: #include @@ -10490,10 +10519,10 @@ Sample::Sample(int x) : Sample(x, 10) cout << "int constructor" << endl; } - Sample::Sample(int x, int y) { - // ortak yapılması gereken şeylerü + // ortak yapılması gereken şeyler + cout << "common codes..." << endl; cout << "int, int constructor" << endl; diff --git a/Python-OzetNotlar-Ornekler.txt b/Python-OzetNotlar-Ornekler.txt index 80681c5..968cf20 100644 --- a/Python-OzetNotlar-Ornekler.txt +++ b/Python-OzetNotlar-Ornekler.txt @@ -24608,7 +24608,6 @@ print(result) # 7 print(s.__len__()) # 7 - #------------------------------------------------------------------------------------------------------------------------ 50. Ders 11/01/2025 - Cumartesi #------------------------------------------------------------------------------------------------------------------------ @@ -25223,7 +25222,6 @@ print(s) #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ -<<<<<<< HEAD Python çok modelli (multi-paradigm) bir programalama dilidir. Biz Python'u istersek sınıf konusuna girmeden tamamen prosedürel bir biçimde kullanabiliriz. NYPT özellikle büyük projelerin mantıksal bakımdan daha kolay ele alınabilmesi düşünülmüştür. Eğer kodlarımız çok uzun ve kapsamlı değilse python'un sınıfsal özelliklerini kullanmamıza gerek olmayabilir. @@ -25233,15 +25231,6 @@ print(s) #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ -======= - Python çok modelli (multi-paradigm) bir programalama dilidir. Biz Python'ı istersek sınıf konusuna girmeden tamamen - prosedürel bir biçimde kullanabiliriz. NYPT özellikle büyük projeleri mantıksal bakımdan oluşturabilmek için düşünülmüştür. - Eğer kodlarımız çok uzun ve kapsamlı değilse python'un sınıfsal özelliklerini kullanmamıza gerek olmayabilir. Tabii daha - önceden de belirttiğimiz gibi Pythoon'ın standart kütüphanesinde hem fonksiyonlar hem de sınıflar bulunmaktadır. Temel veri - yapıları bile (list, tuple, dict gibi) birer sınıf belirttiğine göre Python programcılarının sınıf kavramını biliyor ve sınıfları - kullanabiliyor olması gerekir. - ->>>>>>> 07815e0e300596cd8061392fe1b78f2b90930db0 Daha önceden de belirttiğimiz gibi NYPT'de olgular sınıflarla temsil edilmektedir. Örneğin tarih olgusu Date isimli bir sınıfla, Soket olgusu Socket isimli bir sınıfla, çalışan olgusu Employee isimli bir sınıfla temsil edilebilir. Sınıflardaki metotlar nesnenin özniteliklerini ortak bir biçimde kullanmaktadır. Yani öznitelikler aslında metotlar tarafından ortak