Visual C++’da Sınıf Yapıları (BÖLÜM 3)

19 Tem

Sınıf Yapıları

 

Constructor ve Destructor Fonksiyonları

 

Program geliştirmekte uzun süredir uğraşıyorsanız, programınızın bazı elemanları için hazırlık yapılmasının gerektiğini bilirsiniz. Nesnelerle çalışıyorsanız bu durumla daha da sık karşılaşıyorsunuzdur. Hatta gerçek problemler düşünülürse, her nesne bu tür bir hazırlığa gereksinim duyar. C++’da bu gereksinim, sınıf bildirimi içerisine constructor fonksiyonlarının konulmasıyla karşılanır. Bir sınıfın yapıcısı (constructor), bu sınıfa ait nesnelerin her oluşturuluşunda çağrılır. Böylelikle bir nesne için gerçekleştirilmesi gereken tüm hazırlıklar constructor fonksiyonu tarafından otomatik olarak yapılabilir.

 

            Constructor fonksiyonu ait olduğu sınıf ile aynı addadır ve bu fonksiyonun geri döndüreceği bir veri tipi yoktur. Constructor fonksiyonu olan örnek sınıfı aşağıda inceleyelim.

 

                #include <iostream>

using namespace std;

 

class myclass {

                int a;

public:

                myclass(); //constructor

                void show();

};

 

myclass::myclass()

{

                cout << “constructor’da\n”;

                a=10;

}

 

void myclass::show()

{

                cout << a;

}

 

int main()

{

                myclass ob;

                ob.show();

                return 0;

}

 

            Bu basit örnekte a değeri myclass() constructorı (yapıcısı) tarafından hazır hale getiriliyor. Constructor, ob nesnesi oluşturulurken çağrılır. Nesne, kendisine ait bildirim deyimi istenildiğinde oluşturulur. C++’da, değişkeninin bildirim deyiminin bir “iş deyimi” olması oldukça önemli. C’de program geliştirirken, bildirim deyimleri basitçe değişkenlerin oluşturulduğu yer olarak düşünülebilir. Oysa C++’da, nesne constructor’a, sahip olabileceğinden değişkenin bildirim deyimi dikkate değer sayıda işin yapılmasını sağlayabilir.

 

            myclass()’ı nasıl tanımladığımıza dikkat edelim. Daha önce de söylediğimiz gibi, bu fonksiyonun return (döndürme) tipi yoktur. C++’ın resmi yazılım kurallarına göre constructor’ların return tipi olması doğru değildir.

 

            Global nesneler için nesne constructor’u, bir kere, program ilk çalışmaya başladığında çağrılır. Yerel nesneler için ise constructor, bildirim deyiminin her işletilişinde çağrılır. Constructor’ın tersi destructor’dır. Bu fonksiyon, nesne yok edilirken çağrılır. Nesnelerle çalışıyorsak onların yok edilmeleri esnasında bazı işlemlerin yapılması gerekir. Örneğin, oluşturulduğunda bellekte yer kaplayan bir nesne, yok edilirken bu belleği serbest bırakmak isteyecektir. Destructor da sınıfta aynı addadır, fakat başında ~ işareti vardır. Aşağıdaki sınıfın destructor fonksiyonunu inceleyelim.

 

                #include <iostream>

using namespace std;

 

class myclass {

                int a;

public:

                myclass(); //constructor

                ~myclass(); //destructor

                void show();

};

 

myclass::myclass()

{

                cout << “constructor’da\n”;

                a=10;

}

 

myclass::~myclass()

{

                cout << “Yokediliyor…\n”;

}

 

void myclass::show()

{

                cout << a << “\n”;

}

 

int main()

{

                myclass ob;

                ob.show();

                return 0;

}

 

            Sınıfa ait destructor, nesne yok edilirken çağrılır. Yerel nesneler bulundukları alandan çıkılınca yok edilirken global nesneler program sona erdiğinde yok edilirler. Bir constructor’ın veya destructor’ın adresinin alınması imkansızdır.

 

Parametre Alan Constructor’lar

 

            Bir constructor fonksiyonuna argüman göndermeniz mümkündür. Bunun için constructor fonksiyonunun bildirimine ve tanımına, uygun parametreler eklemeniz yeterlidir. O halde nesneleri deklare ederken argümanları belirleyeceğiz. Bunu yapacağımızı görmek için aşağıdaki örneğe bir göz atalım.

 

                #include <iostream>

using namespace std;

 

class myclass {

                int a;

public:

                myclass(int x); //constructor

                void show();

};

 

myclass::myclass(int x)

{

                cout << “constructor’da\n”;

                a=x;

}

 

void myclass::show()

{

                cout << a << “\n”;

}

 

int main()

{

                myclass ob(4);

                ob.show();

                return 0;

}

 

            myclass’ın parametre aldığı constructor fonksiyonu, myclass()’a gönderilen değer a’nın hazırlığı için kullanılmaktadır. ob’un main()’in içerisinde nasıl deklare edildiğine özellikle dikkat edin. ob’dan sonra gelen parantezlerin içinde verilen 4 değeri, a’ya lk değer vermek için myclass()’ın x parametresine gönderilen argümandır.

 

            Gerçekte parametreli bir constructor’a argüman gönderme işlemi şu notasyonla yapılmalıdır.

 

                myclass ob = myclass(4);

 

            Fakat, C++ programcılarının çoğu, kısa şekli tercih etmektedir. Bu iki yazım şekli arasında, aslında teknik açıdan küçük bir fark bulunmaktadır. Bu fark, consturctor’ların kopyalanmasıyla ilgilidir ve daha sonra ele alınacaktır. Fakat aradaki bu fark konusunda şu anda endişelenmemiz gerekmiyor.

 

Sınıflarda Miras

 

            C++ için miras, bir sınıfın bir diğer sınıfın özelliklerini mirasla alabildiği mekanizmadır. Miras, sınıflar arasında en genelden en özele doğru bir hiyerarşinin oluşmasını sağlar.

 

            Konuya başlamadan önce, sık kullanacağımız iki terimi tanıyalım. Bir sınıf, diğer sınıftan miras aracılığıyla oluşturulurken, ebeveyn sınıfa temel sınıf, mirasla oluşturulan sınıfa ise alt sınıf diyoruz. Genelde miras süreci, temel sınıfın tanımlanmasıyla başlar. Temel sınıf, tüm alt sınıfların sahip olacağı genel özellikleri tanımlar. Esasen temel sınıf, bir takım özelliklerin en genel tanımını temsil etmektedir. Alt sınıf, bu genel özellikleri mirasla alır ve onlara sadece kendisine özgü yeni özellikler ekler.

 

            Bir sınıfın bir diğer sınıftan mirasla oluşmasının ne anlama geldiğini anlamak istiyoruz. Bunun için kısa ve basit bir örnekle işe başlayalım. Bu örnek basit olduğu halde mirasın anahtar özelliklerinden pek çoğunu bize gösterecek. İlk olarak temel sınıfın deklarasyonunu inceleyelim.

 

                // Temel sınıf tanımlanıyor.

                class B {

                               int i;

                public:

                                void set_i (int n);

                               int get_i();

                };

 

Bu temel sınıf kullanılarak bir alt sınıf oluşturuluyor.

 

                // Alt sınıf tanımlanıyor.

                class D : public B {

                               int j;

                public:

                               void set_j (int n);

                               int mul();

};

 

            Bu deklarasyonu daha yakından inceleyelim. D sınıf adından sonra public anahtar kelimesi, derleyiciye, temel sınıfın tüm public elemanlarının alt sınıfın da public elemanları olacağını söylemektedir. Fakat temel sınıfın tüm private elemanları kendisine private (özel) kalır ve alt sınıf onlara doğrudan erişemez.

 

            B ve D sınıflarını kullanan programın tamamı:

 

                // Mirasa basit bir örnek.

                #include <iostream>

                using namespace std;

 

                // Temel sınıfın tanımlanması.

                class B {

                               int i;

                public:

                               void set_i (int n);

                               int get_i();

                };

 

                // Alt sınıfın tanımlanması.

                class D : public B {

                               int j;

                public:

                               void set_j(int n);

                               int mul();

                };

 

                // Temel sınıf için i değerinin verilmesi.

                void B::set_i(int n)

{

                               i = n;

                }

 

                // Temel sınıf için i değerinin döndürülmesi.

                void D::set_j (int n)

                {

                               j = n;

                }

 

                // Temel sınıfın i’si çarpı alt sınıfın j’si değerinin döndürülmesi.

                int D::mul()

                {

                               // Alt sınıf, temel sınıfın public fonksiyonlarını çağırabilir.

                               Return j * get_i();

                }

 

                int main()

{

                               D ob;

 

                               ob.set_i(10); // Temel sınıfta i’ye değer veriliyor.

                               ob.set_j(4); // Alt sınıfta j’ye değer veriliyor.

 

                               cout << ob.mul(); // ekranda 40 gösteriliyor.

 

                               return 0;

                }

 

mul() fonksiyonunu nasıl tanımladığımıza bir bakalım. Bu fonksiyonda, B temel sınıfına ait olan, D’ye ise ait olmayan get_i() fonksiyonunu herhangi bir nesneye bağlamadan çağırdığımıza dikkatinizi çekmek istiyorum. Bu, mümkündür, çünkü B’nin public üyeleri D’nin de public üyeleri olmuştur. Fakat, mul()’un doğrudan i’ye erişmek yerine get_i()’yi çağırmak zorunda olmasının nedeni şudur: Temel sınıfa private olarak ait olan üyeler (bu durumda i), bu sınıfa özel üyeler olarak kalmaktadır ve herhangi bir alt sınıf tarafından bu üyelere erişilemez. Bir sınıfın private üyelerine alt sınıflar tarafından erişilememesi, depolama özelliğinin sürdürülmesi için gereklidir. Eğer bir sınıfa ait private üyeler, bu sınıfın mirasla alınması ile public yapılabilseydi, depolama (encapsulation) özelliği ortadan kalkardı.

 

            Temel sınıfın mirasla alınması, genelde şu notasyonla yapılır.

 

                class alt-sınıf-ismi:erişim-türü alt-sınıf-ismi {

                .

                .

                .

                };

 

        Burada erişim-türü yerine üç anahtar kelimeden birini yazacağız: public, private veya protected.

 

Nesne İşaretçileri

 

            Şimdiye kadar nesnelere ait üyelere nokta işaretini kullanarak erişiyorduk. Bu yöntem, tek nesneyle çalışıyorsak doğrudur. Fakat nesnelere erişirken bu nesneye atanan bir işaretçiyi de kullanabiliriz. Erişimi işaretçi ile gerçekleştiriyorsak, nokta işareti yerine ok işareti (->) kullanacağız..

 

            Nesne işaretçilerini herhangi bir değişkeni nasıl deklare ediyorsak o şekilde deklare edeceğiz. Bunun için sınıf adını belirledikten sonra değişken adının başına bir yıldız işareti (asteriks) koyacağız. Nesnenin adresini elde etmek için ise adın başına & işaretini koyacağız, tıpkı herhangi bir tipteki değişkenin adresini alırken yaptığımız gibi.

 

            Diğer tiplerin işaretçileri gibi, nesne işaretçileri de arttırıldıkları zaman bu tipteki diğer nesneyi işaret edeceklerdir. Basit bir örnekle açıklayalım.

 

                #include <iostream>

                using namespace std;

 

                class myclass {

                int a;

public:

                myclass (int x); // constructor

                int get();

};

 

myclass::myclass(int x)

{

                a = x;

}

 

int myclass::get()

{

                return a;

}

 

int main()

{

                myclass ob(120); // Nesne oluşturuluyor.

                myclass *p; // Nesne için işaretçi oluşturuluyor.

 

                p = &ob; // ob’nin adresi p’ye atanıyor.

 

                cout << “Nesneyi kullanan değer: ” << ob.get();

                cout << “\n”;

 

                cout << “İşaretçiyi kullanan değer: ” << p->get();

 

                return 0;

}

 

Örnekteki myclass *p; declarasyonu myclass sınıfına ait bir nesneye işaretçi oluşturuyor. Nesne işaretçisi oluşturulduğunda bir nesne oluşturulmaz, sadece mevcut nesne için bir işaretçi oluşturulur, bu durumu kavramamız çok önemli, ob’un adresi aşağıdaki bildiri ile p’ye konuluyor.

 

            p = &ob;

 

            Programda, son olarak, nesne üyelerine bir işaretçi üzerinden nasıl erişildiğini görüyoruz.

 

Sınıf ve Yapı Arasındaki Bağlantı

 

            Sınıfların notasyonunun yapıların notasyonuna benzediğini gördük. Sınıflar ile yapıların gerçekte aynı kapasiteye sahip olduğunu öğrenince şaşırabilirsiniz. C++’da, sınıf tanımları gibi yapı tanımları da, constructor ve destructor fonksiyonlarının yanı sıra tüm üye fonksiyonlarını içerecek şekilde genişletilmiştir. Hatta, bir yapı ile bir sınıf arasındaki tek fark, sınıflara ait olan üyelerin varsayılan olarak private (özel), yapılara ait üyelerin ise yine varsayılan olarak public (genel) olmasıdır. Yapıların genişletilmiş yazılım şeklini aşapıda görüyoruz.

 

                struct tip-ismi {

                               // public fonksiyonlar ve veri üyeleri

                private:

                               // private fonksiyonlar ve veri üyeleri

} nesne-listesi;

 

            Hatta, C++’ın resmi yazılım kurallarına göre gerek struct, gerekse class, yeni sınıf tipleri oluşturur. Yeni bir anahtar kelimeyle karşılaştık: private. Bu kelime derleyiciye, kendisinden sonra gelen üyelerin bu sınıfa private (özel) olduğunu söylemektedir.

 

            Yüzeysel olarak baktığımızda, sınıfların ve yapıların fiilen eş kapasitede olması bir fazlalık gibi görünebilir. C++’la yeni tanışanlar bu durumun nedenini merak ediyor olabilirler. Hatta, class anahtar kelimesinin gereksiz olduğu da pek sık karşılaşılan bir tartışma konusudur.

            Bunun bir “kuvvetli” bir de “zayıf” sebebi vardır. “Kuvvetli” olan sebep (yada zorlayıcı olan sebep) C’ye uyumluluğun sağlanmasıdır. C++ programında, C yapıları geçerlidir. C’de yapılara ait tüm üyeler varsayılan olarak public’tir ve bu durum C++’da da sürdürülmek istenmiştir. Daha da ötesi, class yazım şekli olarak struct’dan ayrı bir varlık olduğundan, sınıf tanımı, C’nin yapı tanımına uymayacak şekilde genişlemekte serbesttir. Bu ikisi farklı şeyler olduğu sürece, C++’ın geleceği uyumluluk sorunlarıyla kısıtlanmamış olmaktadır.

 

            Birbirine benzeyen sınıf ve yapı kavramlarının her ikisini de kullanmanın “zayıf” nedeni ise C++’da üye fonksiyonların içerilmesi için yapı tanımını genişletmenin hiçbir dezavantajı bulunmamasıdır.

 

            Her ne kadar yapılar sınıflarla aynı kapasiteye sahip olsalar da, programcıların çoğu C’ye olan uyumluluğu bozmamak için yapıların kullanımını sınırlamışlardır ve onları üye fonksiyonları içermek için kullanmamaktadırlar. Programcıların çoğu, hem veri, hem de kod içeren nesneleri tanımlarken class anahtar kelimesini kullanırlar fakat bu sadece stille alakalı bir meseledir ve tercihinize kalmıştır.

 

            Eğer sınıflarla yapılar arasındaki bağlantıyı ilgi çekici bulduysanız C++’ın bir diğer özelliği de ilginizi çekecek demektir: bileşimler ve sınıflarda birbiriyle bağlantılıdır. C++’da bileşimler, hem fonksiyon hem de veri içerbilen sınıf tüplerini tanımlar. Bileşimler bu açıdan yapılara benzemektedirler, varsayılan olarak tüm üyeleri private belirleyici kullanılana kadar public’tir. Bir bileşimde tüm veri üyeleri aynı bellek yerini paylaşır (tıpkı C’de olduğu gibi). Bileşimler, constructor ve destructor fonksiyonlarını içerebilirle C bileşenleri neyse ki, C++ bileşimleri ile uyumludurlar.

 

            Olaya yüzeysel olarak baktığımızda sınıfların ve yapıların ikisinin birden kullanılıyor olması fazlalıkmış gibi görünse de, durum bileşimler için böyle değildir. Nesneye dayalı bir dilde depolama özelliğinin korunması önemlidir. Böylelikle bileşimin kod ve veriyi bağlayabilme kabiliyeti, içindeki tüm verilerin aynı yeri paylaştığı sınıf tipleri oluşturmamıza imkan verir. Bunu, sınıfları kullanarak yapamayız.

 

            Bileşimler için C++’da çeşitli sınırlamalar bulunmaktadır. Başka sınıflardan miras aracılığıyla oluşturulamazlar ve başka sınıfların temel sınıfı olamazlar. Bileşimlerin static üyeleri olamaz ve constructor veya destructor’ı olan herhangi bir nesnede içeremezler. Bileşimin kendisinin constructor ve destuctor’ı olabilir. Son olarak ta, bileşimler gerçek fonksiyonlara sahip olamazlar.

 

            C++’da anonim bileşim adı verilen özel bir bileşim tipi vardır. Anonim bileşimlerin tip adları yoktur ve bu bileşim türü için hiçbir değişken deklare edilmez. Aksine, anonim bileşim, derleyiciye kendine ait olan üyelerin aynı bellek yerini paylaşacaklarını söyler. Fakat, diğer açılardan, bu üyeler, normal değişkenler gibi rol oynarlar ve onlara da o şekilde davranılır. Yani, üyelere, doğrudan, nokta işareti kullanılmaksızın erişilir. Aşağıdaki programı inceleyerek bunu kavramaya çalışalım.

 

                union { // bir anonim bileşim

                               int i;

                               char ch[4];

                };

                i = 10; // i’ye ve ch’ye doğrudan erişiliyor

                ch[0] = ‘X’;

 

            Burada i ve ch’ye doğrudan erişiyoruz, çünkü bu değişkenler herhangi bir nesnenin parçası değildirler. Bellekte aynı yeri paylaşırlar.

 

            Anonim bileşimi kullanarak derleyiciye iki veya daha fazla sayıda değişkenin bellekte aynı yeri paylaşmasını istediğimizi kolayca söyleyebilir. Bu özel nitelik haricinde, anonim bileşimin üyeleri, tamamen diğer değişkenler gibi davranırlar.

 

            Anonim bileşimlerin bu tek özellikleri yanında, normal bir bileşime konulan tüm sınırlamalar geçerlidir. Global bir anonim bileşimi, static olarak deklare etmeliyiz. Anonim bileşimler private üyelere sahip olamazlar ve onlara ait üyelerin adları aynı alandaki diğer belirleyicilerle çakışmamalıdır.

 

            Sınıf oluşturmak için struct kullanan kısa bir program

 

                #include <iostream>

                #include <cstring>

                using namespace std;

 

                // sınıf tipi tanımlamak için struct kullanılıyor

                struct st_type {

                               st_type ( double b, char *n);

                               void show();

                private:

                               double balance;

                               char name[40];

                };

 

                st_type::st_type (double b, char *n)

                {

                               balance = b;

                               strcpy(name, n);

                }

                void st_type::show()

                {

                               cout << “İsim: ” << name;

                               cout << “: $” << balance;

                               if (balance < 0.0 ) cout << “***”;

cout << “\n”;

}

 

                int main ()

{

                               st_type acc1 (100.12, “Johnson”);

                               st_type acc2 (-12.34, “Hedricks”);

 

                               acc1.show();

                               acc2.show();

                               return 0;

                }

 

            Daha önce belirttiğimiz gibi, yapıya ait üyeler varsayılan olarak public’tir. Private üyelerin deklare edilmesi için private anahtar kelimesini kullanmalıyız.

 

 C ve C++ yapıları arasındaki bir diğer farka daha dikkatinizi çekmek istiyorum. C++’da yapı etiket-ismi, nesnelerin deklare edilmesinde kullanılabilecek bir tip adı haline gelmektedir. C’de etiket-isminin bir tip olması için struct anahtar kelimesinin kendisinden önce gelmesi gerekmektedir.

 

            Aynı programın sınıf kullanılarak yazılmış hali.

                #include <iostream>

                #include <cstring>

                using namespace std;

 

                class cl_type {

                               double balance;

char name[40];

                public:

                               cl_type (doble b, char *n);

void show();

                };

 

                cl_type::cl_type (double b, char *n)

                {

                               balance = b;

                               strcpy(name, n);

                }

                void cl_type::show()

                {

                               cout << “İsim: ” << name;

                               cout << “: $” << balance;

                               if (balance < 0.0 ) cout << “***”;

cout << “\n”;

}

 

                int main ()

{

                               cl_type acc1 (100.12, “Johnson”);

                               cl_type acc2 (-12.34, “Hedricks”);

 

                               acc1.show();

                               acc2.show();

                               return 0;

                }

 

Nesne Atamak

 

            Bir nesne aynı tipten başka bir nesneye atanabilir. Normalde, nesne diğer nesneye atandığında tüm veri üyelerinin bit bit kopyası alınmaktadır. Örnek olarak o1 adındaki nesne o2’ne kopyalanır. Bunu aşağıdaki programda inceleyelim.

 

                // Nesne atamalarına bir örnek.

                # include <iostream>

                using namespace std;

 

                class myclass {

                               int a, b;

public:

                void set(int i, int j) { a=i; b= j; }

                void show() { cout << a << “  “ << b << “\n”; }

};

 

int main()

{

                myclass o1, o2;

 

                o1.set(10, 4);

 

                // o1’i o2’ye ata

                o2 = o1;

                o1.show();

                o2.show();

 

                return 0;

}

 

            Burada o1 nesnesi, kendisine ait olan a ve b üye değişkenlerine sırasıyla 10 ve 4 değerlerini atar. Daha sonra, o1, o2’ye atanır. Bu, o1.a’nın o2.a’ya atanmasını ve o1.b’nin de o2.b’ye atanmasına neden olur. Programı çalıştırdığınızda, ekranda aşağıdaki sonucu göreceksiniz.

 

10     4

10     4

 

Bir nesneyi diğerine atadığınızda bu nesnelerin içerisindeki verilerin eşitlendiğini aklınızda tutun. İki nesne halen birbirinden ayrı durumdadır. Örneğin atama işleminden sonra o1.a’ya değer atamak için o1.set()’in çağrılması, o2 ve ona ait a değeri üzerinde bir etki yapmaz.

 

Fonksiyonlara Nesne Aktarmak

 

Nesneler, tıpkı diğer veri tiplerinin aktarılmasında olduğu gibi argümanlar halinde fonksiyonlara aktarılabilir. Fonksiyonun parametresi sınıf tipinde bildirilir ve sonra fonksiyonun çağrılışında bu sınıfa ait bir nesne argüman olarak kullanılır. Fonksiyona diğer veri tiplerinde olduğu gibi varsayılan olarak nesnelerin değerleri aktarılır.

 

                #include <iostream>

using namespace std;

 

class samp {

                int i;

public:

                samp (int n) { i = n; }

                int get_i() { return i; }

};

 

// o.i’nin karesi döndürülür.

int sqr_it (samp o)

{

                return o.get_i() * o.get_i();

}

 

int main()

{

                samp a(10), b(2);

                cout << sqr_it(a) << “\n”;

                cout << sqr_it(b) << “\n”;

                return 0;

}

 

            Bu program, i tamsayısını içeren, samp adlı bir sınıf oluşturur. sqr_it() fonksiyonunun samp tipinde bir argümanı vardır ve bu fonksiyon nesneye ait i değerinin karesini döndürür. Bu program, ekranda 4 ve 100 değerlerini gösterecektir.

 

 

 

Fonksiyonlardan Nesne Döndürmek

 

            Tıpkı fonksiyonlara nesne gönderebildiğimiz gibi, fonksiyonlardan nesne de döndürebiliriz. Bunun için önce fonksiyonu sınıf tipi döndürür şekilde bildirmeniz gerekir. İkinci olarak da bu tipte bir nesneyi normal return deyimini kullanarak döndürürüz.

 

            Fakat fonksiyonlardan nesne döndürülmesi konusunda anlaşılması gereken önemli bir nokta daha var: Bir nesne fonksiyon tarafından döndürüldüğünde, otomatik olarak, döndürülen değeri saklayan geçici bir nesne oluşturulur. Gerçekte fonksiyon tarafından döndürülen nesne budur. Değer döndürüldükten sonra bu nesne yok edilir.

 

                // Nesne döndürülüyor

                #include <iostream>

                #include <cstring>

                using namespace std;

 

                class samp {

                               char s[80];

                public:

                               void show() { cout << s << “\n”; }

                               void set(char *str) { strcopy(s, str); }

                };

 

                // samp tipinde bir nesne döndürülüyor

                samp input()

                {

                               char s[80];

                               samp str;

                               cout << “Bir katar girin: ”;

                               cin >> s;

                               str.set(s);

                               return str;

                }

 

                int main()

                {

                               samp ob;

                               // Döndürülen nesne ob’a atanıyor.

                               ob = input();

                               ob.show();

                return 0;

}

 

        Bu örnekte input() fonksiyonu, str adında yerel bir nesne oluşturur ve sonra da klavyeden bir katar okur. Bu katar str.s’in içine kopyalanır. Ve sonra str, fonksiyon tarafından döndürülür. Nesne, döndürüldükten sonra main()’in içerisindeki ob’a atanır.

 

Arkadaş Fonksiyonlar

 

            Bir fonksiyonun üyesi olmadığı bir sınıfa ait private üyelere erişim hakkı olmasını istediğiniz zamanlar olacaktır. Bunun için C++ size arkadaş (friend) fonksiyonlarını sunmaktadır. Arkadaş fonksiyonları sınıfa ait değildir, ama bu fonksiyonların o sınıfa ait private elemanlara erişim hakkı vardır.

 

            Arkadaş fonksiyonlarının işimize yaradığı iki durum vardır. Operatörlerin üst üste yüklenmesi ve belli tiplerde I/O fonksiyonlarının oluşturulması. Arkadaş fonksiyonlarının bu iki şekilde nasıl kullanıldığını görmek için bir süre daha beklemeniz gerekecek. Fakat, burada ele alacağımız üçüncü bir kullanım şekli daha var: Bir fonksiyonun iki veya daha fazla sınıfın private üyelerine erişmesinin istendiği durum.

 

            Bir arkadaş fonksiyonu düzgün ama üye olmayan bir fonksiyon olarak tanımlanır. Fakat bu fonksiyonun, kendisine arkadaş olacağı sınıf bildiriminin içerisinde friend anahtar kelimesi ile belirtilen bir prototipi mevcuttur. Bunun nasıl çalıştığını anlamak için aşağıdaki bu kısa örneği inceleyelim.

 

            // Arkadaş fonksiyonlarına bir örnek

                #include <iostream>

                using namespace std;

                class myclass {

                               int n, d;

                public:

                               myclass (int i, int j) { n= i; d = j; }

                               //myclass’ın arkadaş fonksiyonun deklare edilmesi

                               friend int isfactor(myclass ob);

                };

/* Arkadaş fonksiyonun tanımı. Eğer n d’nin tam katı ise true değerini döndürür. friend anahtar kelimesinin isfactor()’ün tanımlanması sırasında kullanılmadığına dikkat edin.

                /*

                int isfactor (myclass ob) {

                               if (!(ob.n % ob.d)) return 1;

                else return 0;

}

int main() {

                myclass ob1(10,2), ob2(13, 3);

                if (isfactor(ob1)) cout << “10, 2’nin katıdır\n”;

                else cout << “10, 2’nin katı değildir\n”;

                if (isfactor(ob2)) cout << “13, 3’ün katıdır\n”;

                else cout << “13, 3’ün katı değildir\n”;

                return 0;

}

 

            Bu örnekte myclass kendi yapıcı fonksiyonunu ve sınıf bildirimi içerisindeki arkadaş isfactor()’ı bildirmektedir. isfactor(), myclass’ın arkadaşı olduğundan isfactor()’un onun private üyelerine erişim hakkı vardır. Bu, isfacotr()’un içerisinde ob.n ve ob.d’ye doğrudan gönderme yapabilmesinin nedenidir.

 

            Bir arkadaş fonksiyonunun arkadaş olduğu sınıfa ait olmadığının anlaşılması önemlidir. Böylece bir arkadaş fonksiyonunu nesne adını ve sınıf üyesi erişim operatörünü (nokta veya ok operatörü) kullanarak çağırmamız mümkün değildir. Örneğin bir önceki örnek için aşağıdaki deyim yanlıştır.

 

                ob1.isfactor(); // yanlış; isfactor() üye bir fonksiyon değildir.

 

            Halbuki arkadaşlar normak bir fonksiyon gii çağrılırlar.

 

            Bir arkadaş fonksiyonu her ne kadar arkadaş olduğu sınıfın private elemanlarının bilgisine sahip olsa da onlara sadece bu sınıfa ait bir nesne üzerinden erişebilir. Yani myclass’ın n veya d’ye doğrudan gönderme yapabilen üye fonksiyonlarının aksine, bir arkadaş fonksiyonu bu değişkenlere sadece, arkadaş fonksiyonu içerisinde bildirilmiş veya arkadaş fonksiyona gönderilmiş bir nesne aracılığıyla erişilebilir.

 

            Arkadaşlar sınıflara ait değillerdir, bu yüzden arkadaş oldukları sınıfa ait bir veya daha fazla nesne kendilerine gönderilecektir. Aynı şey isfactor() için de geçerlidir. Bu fonksiyona ob adında myclass’a ait bir nesne gönderiyoruz. Fakat isfactor(), myclass’ın arkadaşı olduğundan, ob’nin private elemanlarına erişebilir. isfactor(), myclass’a arkadaş olmasaydı n ve d myclass’ın private üyeleri olduğu sürece ob.d veya ob.n’ye erişemeyecekti.

 

            Arkadaş fonksiyonların miras yoluyla aktarılmaları da mümkün değildir. Yani temel sınıfın arkadaş fonksiyonu türetilmiş sınıfın da arkadaşı değildir.

 

            Bu fonksiyonlar hakkındaki bir diğer önemli nokta da arkadaş fonksiyonlarının birden fazla sınıfla arkadaş olabilmesidir.

Bir Yanıt to “Visual C++’da Sınıf Yapıları (BÖLÜM 3)”

  1. murat şahin Ağustos 7, 2010 4:15 pm #

    çok sağolun çok işe yarar bilgiler var
    emeğinize sağlık

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Connecting to %s