PDA

Tam Sürümünü Görmek İçin : static veri uyesi


quasimodo
09/05/2007, 11:59
Bir class in icinde static bir veri uyesi tanimladigimizda.

Bu elemana mutlaka ilk deger vermemiz gereklimidir?
Ilk deger vermezsek degeri 0 olurmu?
Ilk deger verme islemini class taniminda yapabiliyormuyuz?


lektroon
09/05/2007, 15:27
Mutlaka bir ilk deger vermen gerek bunu da class davranisini tanimladigin *.cpp dosyasinda yapman gerekl. Ornegin,

//C.h
class C
{
static int sta;
};

Bu durumda:
//C.cpp
int C::sta = 0;

Demen lazim. Baska turlu olmaz, bu degiskeni bir yerde kullanirsan linker hatasi alirsin.

acehreli
09/05/2007, 21:01
Dogrusu lektroon'u yaptigi gibi ama "=0" kismini yazmasak da degeri diger static nesneler gibi 0 olur.

Ben de her zaman ilk degerini acikca yazarim.

Ali

quasimodo
10/05/2007, 14:23
iste anlamadigim noktaya geldik o icerideki
veri uyesi private ama biz ona disaridan ilk deger
veriyoruz. farkli bir .cpp modulunden static oldugu
icin mi erisebiliyoruz ona?

Birde static fonksiyonlar sadece nesne olusturulmadiginda
o static veri uyesine erismek icin mi tanimlanir?
Yani nesne olusturuldugunda biz static olmayan bir fonksiyonlada
o static veri uyesine erisebilir degil mi?

static bir fonksiyon static olmayan bir uye fonksiyon cagirabilir mi?

Son olarakta static fonksiyonlar bizim veri uyelerine erisebilirmi.
this isaretcisi yok ya oyuzden soruyorum.

Revne
10/05/2007, 15:48
Birde static fonksiyonlar sadece nesne olusturulmadiginda
o static veri uyesine erismek icin mi tanimlanir?

static fonksiyon veya degisken kullanımının bir cok sebebi vardır. Ama bir nesne ne kadar cok static uye icerirse o kadar Multi-Threading den uzaklasır..


Yani nesne olusturuldugunda biz static olmayan bir fonksiyonlada
o static veri uyesine erisebilir degil mi?
evet erişebilir.


static bir fonksiyon static olmayan bir uye fonksiyon cagirabilir mi?

Son olarakta static fonksiyonlar bizim veri uyelerine erisebilirmi.
this isaretcisi yok ya oyuzden soruyorum.
hayır erişemez...

Fakat bunun icin genelde Static fonksiyonlara nesne parametre olarak verilir..
Boylece static olan fonksiyonda static olmayan veri işlenir...

class Insan
{
private:
long x,y;
public:
void sagAyak();
void solAyak ();
static void yuru(Insan& ins){
ins.sagAyak();
ins.solAyak();
}
}


Kolay Gelsin..

quasimodo
11/05/2007, 14:58
Farkli bir cpp modulunden static degiskene nasil erisebiliyoruz?
O private tanimlanmamis mi. static degiskenlerde public private
nin anlami ney?



Birde farkli modulde ilk deger verilirken neden basina int i koyuyoruz?

shurzan
11/05/2007, 15:49
Aslında çok zor bir konu olmasada uzun uzadıya yazmak yerine aşağıda Necati ERGİN hocanın ders notlarından alıntı yapacağım.

------------------------------------------------------------------------------
Sınıfın bir elemanı sınıf bildirimi içinde başına static anahtar sözcüğü getirilerek bildirilirse bir çeşit sınıfa özgü global değişken gibi ele alınır. Böyle bir elemana sınıfın static elemanı denir. Sınıfın static elemanı ile global değişkenler arasında amaç koda yazım biçimi bakımından hiçbir fark yoktur;

yalnızca sınıfın static elemanları sınıf isimleri ile kombine edilerek amaç kod içinde yazılırlar. static elemanlar sınıf nesnesi içinde bir yer kaplamazlar, onlar dışarıda global değişkenler gibi saklanırlar. Yalnızca mantıksal açıdan sınıfla ilişkilendirilirler.

Sınıfın static elemanları global değişkenlerde olduğu gibi dışarıda ayrıca tanımlanmak zorundadır.Tanımlama işlemi, bildirim sınıfın hangi bölümünde yapılmış olursa olsun gereklidir ve geçerlidir.

Aşağıdaki main işlevini inceleyin:

int main()
{
Person x("Ali Serçe", 25);

cout << x.count; // 1

Person y("Ahmet Altıntartı", 32);
cout << y.count; // 2
return 0;
}




Sınıfın statik elemanları, sınıf türünden nesne yaratıldığında o nesnenin içinde yer almazlar. Yani sınıfnesnesinin sizeof değerini büyütmezler. Sınıfın statik bir elemanından yalnızca bir tane bulunur. static elemanları aslında global değişkenlerdir; yalnızca mantıksal bakımdan sınıf ile ilişkilendirilmiştir. statik elemanlara tanımlama sırasında ilkdeğer de verilebilir. Ancak ilkdeğer

verilmemişse, global ve static yerel değişkenlerde olduğu gibi içlerinde sıfır bulunur. Sınıfın statik bir elemanı sınıf dışında tanımlanmaz ise static elemanın kullanılması durumunda derleme zamanında hata oluşmaz. Hata bağlama aşamasında oluşur.

statik elemanlarına dışarıdan ilgili sınıf türünden nesne, gösterici ya da referans yoluyla erişilebilir. Tabii statik elemanının public bölümde bildirilmiş olması gerekir.
Burada

x.count ile erişilen count değişkeni ile y.count ile erişilen count değişkeni aynı değişkenlerdir.
Sınıfın statik bir elemanına hangi sınıf nesnesi ile erişildiğinin bir önemi yoktur. Bu yüzden statik elemanlara sınıf nesnesi olmadan da sınıf ismi ve çözünürlük işleci ile erişilebilir. Tabii böyle bir erişimin geçerli olması için bildirimin public bölümde yapılmış olması gerekir. Aşağıdaki main işlevini inceleyiniz.

int main()
{
Person x("Ali Serçe");

cout<< Person::count << endl; // 1

Person y("Ahmet Altıntartı");
cout<< Person::count << endl; // 2
return 0;
}






Örnekte erişim Person::count biçiminde hiç sınıf nesnesi kullanılmadan yapılmış. count public bölümde olduğu için erişim geçerlidir. static elemanlarına üye işlevler içinde normal bir elemanıymış gibi doğrudan erişilebilir. Örneğin Person sınıfının kurucu işlevi içinde erişim bu biçimdesağlanıyor:

Person::Person(const char *nm, int n)
{
name = new char[strlen(nm) + 1];

strcpy(name, nm);

no = n;
++count; // statik elemana üye işlevler içinde doğrudan erişilebilir
}






Sınıfın statik elemanı bir dizi ya da gösterici olabilir. Eğer dizi ise bildirim sırasında dizinin boyutu belirtilmeyebilir. Ancak dizinin tanımlaması sırasında dizinin boyutu belirtilmelidir. Sınıfa ilişkin statik bir diziye tanımlama sırasında küme ayraçları içinde ilkdeğer de verilebilir. Tabi bu durumda dizi boyutu belirtilmeyebilir. Örneğin,

class Date {
private:
int day, month, year;

static char *day_tab[ ];

public:
// …
};
char * Date::day_tab[12] = {"Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma","Cumartesi", "Pazar"};







Sınıfın statik elemanları yalnızca bir kez tanımlanmalıdır. Bunun için tanımlama işlemi bir .CPP dosyası içinde yapılmalıdır. Eğer tanımlama.h dosyası içinde yapılır ve bu dosya da proje içinde birden fazla yerde eklenirse bağlama zamanında hata oluşur. Sınıfın statik bir elemanını kullanmak için o sınıf türünden nesne tanımlamaya gerek yoktur. Yani sınıf türünden hiçbir nesne tanımlanmasa bile sınıfın statik elemanları yine de kullanılabilir.

shurzan
11/05/2007, 16:08
Sınıfın statik Üye İşlevleri
Sınıf bildirimi içinde bir işlev, başına static anahtar sözcüğü getirilerek bildirilebilir. Böyle işlevlerle sınıfın statik üye işlevleri denir. Sınıfın statik üye işlevleri global bir işlev gibidir. Ancak mantıksal bakımdan sınıfla ilişkilendirilmiştir. Bu işlevlere this göstericisi geçirilmez. Sınıfın statik üye işlevlerine this göstericisinin geçirilmemesi, o işlevler içinde sınıfın elemanlarına ve diğer üye işlevlere doğrudan erişilemeyeceği anlamına gelir.

Sınıfın statik üye işlevi her ne kadar global bir işlev gibiyse de tanımlanması üye işlevlerde olduğu gibi sınıf ismi belirtilerek yapılır. Date sınıfında bazı değişiklikler yapıyoruz:

#include <iostream>

using namespace std;


class Date {
private:

int day, month, year;

static int mon_days[12];
static char *mon_tab[12];
static char *day_tab[7];
public:
enum Days {Sunday , Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
Date();
Date(int, int, int);
void display() const;
void display_text() const;
long totaldays() const;
void revdays(long);
int weekday() const;
static bool isleap(int); // static üye işlev
};



Önceki biçimde bir yılın artık yıl olup olmadığını bulan isleap isimli işlev normal bir global işlevdi. Ancak bu işlevin yalnızca tarih işlemleriyle anlamlı bir işlevi vardır. O halde mantıksal bakımdan Date sınıfı ile ilişkilendirilip algılama kuvvetlendirilmiştir. isleap işlevi dışarıda şöyle tanımlanabilir.





bool Date::isleap(int year)
{
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}


static anahtar sözcüğünün yalnızca bildirim sırasında yazıldığına, tanımlama sırasında yazılmadığına dikkat ediniz. isleap içinde sınıfın day, month ve year elemanlarına doğrudan erişmeyiz; diğer üye işlevlerini sınıf nesnesi olmadan doğrudan çağıramayız. Gördüğünüz gibi dışarıdaki sınıf ile mantıksal bakımdan ilişkili olan ama sınıfın elemanlarını ve üye işlevlerini kullanmayan global işlevler sınıfın statik işlevi yapılabilirler. Sınıfın statik üye işlevi bir sınıf nesnesi, göstericisi ya da referansıyla çağrılabilir. Ya da eğer sınıfın public bölümündeyse çağrı doğrudan sınıf ismi belirtilerek :: çözünürlük işleci ile de yapılabilir.


int main()
{
Date x;

if (x.isleap(1996))

cout << "is a leap year" << endl;

else
cout << "is not a leap year" << endl;
if (Date::isleap(2000))
cout << "is a leap year" << endl;
else
cout << "is not a leap year" << endl;
return 0;
}




Burada x.isleap ve Date::isleap biçiminde iki çağrı yapılmıştır. isleap sınıfın public bölümünde olduğu için her iki çağrı biçimi de geçerlidir. Ancak yine de statik üye işlevleri sınıf nesnesi kullanmadan sınıf ismiyle çağrılması tavsiye edilir. Ne de olsa çağrıda kullanılan sınıf nesnesinin hiçbir özel anlamı yoktur. Sınıfın statik işlevi sınıfın bir üye işlevi içinde doğrudan çağrılabilir. Örneğin totaldays işlevi içinde isleap doğrudan çağrılıyor.

shurzan
11/05/2007, 16:21
Sınıfın statik bir üye işlevi içinde statik elemanları doğrudan kullanılabilir. Sınıfın statik elemanlarının sınıf nesnesi içinde yer kaplamadığını anımsayınız. Bu durumda statik elemanlara erişmek için this göstericisi gerekmez, değil mi? Aşağıda bu biçimde kullanıma ilişkin bir örnek veriyoruz:

#include <iostream>
#include <cstring>
using namespace std;
class Person {
private:
char *name_;

int no_;
static int count_;
public:
Person(const char *nm, int n);
~Person();
void display() const;
static int get_count();
};
int Person::count_ = 0;
Person::Person(const char *nm, int n)
{
name_ = new char[strlen(nm) + 1];
strcpy(name_, nm);
no_ = n;
++count_;
}
void Person::display() const
{
cout << name_ << endl;
cout << no_ << endl;
}
int Person::get_count()
{
return count_;
}
Person::~Person()
{
delete [] name_;
}
int main()
{
Person x("Kaan Aslan", 25);
Person y("Ali Serçe", 34);
cout << Person::get_count();
return 0;
}




Bu örnekte daha önce vermiş olduğumuz Person isimli sınıfta bazı değişiklikler yaptık. Örneğin statik count_ elemanını sınıfın private bölümüne yerleştirdik, bu elemanın değerinin elde edilmesi için get_count isimli statik bir üye işlev ekledik. get_count işlevi yalnızca sınıfın statik elemanı olan count_ ile ilişkili olduğuna dikkat ediniz. Sınıfın statik üye işlevi ile aynı isimde ve parametre yapısına sahip global bir işlev bulunabilir. Çünkü sınıfın statik işlevleri sınıf ismi ile birleştirilerek amaç kod içine yazılırlar. Sınıfın kurucu ve sonlandırıcı işlevleri statik üye işlev olamaz. Bu işlevlerin yaratılan ve yok edilen nesne üzerinde doğrudan işlem yapmak zorunda olduğunu düşünürsek static üye işlev olmamaları gerektiği de açıktır, değil mi?

Statik bir üye işlev const bir üye işlev olarak da bildirilemez. Zira const bir üye işlev, this göstericisinin gösterdiği nesne const olan bir üye işlev anlamına gelir. statik üye işlevler this göstericisine sahip olmadıklarına göre, const da olamazlar. Sınıfın statik üye işlevleri sonuçta sınıfa ilşkin bir üye işlev olduğu için, statik üye işlevleri içinde sınıfa ilişkin bir nesne tanımlandığında o nesne yoluyla dışarıdan sınıfın her bölümüne erişilebilir.

class Sample {
int x;
public:
static void func();

};

void Sample::func()
{
Sample sam;
sam.x = 20; //Geçerli
//...
}

shurzan
11/05/2007, 16:22
Bazen de static üye işlevler özel amaçları gerçekleştirmek için belirli kod kalıplarında kullanılırlar.Örneğin bir sınıf türünden yalnızca dinamik nesnelerin yaratılmasına izin verilmesi istendiğini düşünelim. DynamicOnly bir sınıf olmak üzere DynamicOnly d; biçiminde bir nesne yaratılmasına engel olunsun. DynamicOnly sınıfına ilişkin tüm nesnelerin dinamik olarak, yani new işleciyle oluşturulması bir zorunluluk olsun. Bu nasıl sağlanabilir? Sınıfın kurucu işlevleri sınıfın private bölümüne yerleştirilirse DynamicOnly d; biçiminde bir tanımlama derleme zamanında hata oluşumuna neden olur değil mi? Ancak bu durumda new işleciyle de nesne yaratılamaz. Hatırlayacağınız gibi new işleciyle bir sınıf nesnesi yaratıldığında önce sınıf nesnesinin kaplayacağı yer kadar dinamik bir alan ayrılıyor daha sonra bu alanın başlangıç adresi sınıfın kurucu işlevine this adresi olarak geçiriliyordu. Kurucu işlev sınıfın private bölümde olduğuna göre nesne yaratılması yine mümkün olmaz değil mi?

Ancak sınıfın kurucu işlevi private bölümde ise, ve sınıfın bir üye işlevi, içinde sınıfın başka bir private bir üye işlevi çağırılabildiğine göre , DynamicOnly sınıfının bir üye işlevi içinde dinamik bir nesne yaratılabilir. Peki üye işlevi bir sınıf nesnesi ile çağırmak gerektiğine göre, böyle bir sınıf nesnesi nasıl elde edilecek?

İşte bu noktada static bir üye işlev kullanılabilir: Dinamik bir sınıf nesnesinin adresi ilegeri dönen statik bir üye işlev:

#include <iostream>
using namespace std;

class DynamicOnly{
DynamicOnly (); //başlangıç işlevi private bölümde.



public:
static DynamicOnly *create_object();

};
DynamicOnly * DynamicOnly::create_object()
{
return new DynamicOnly;//Geçerli çünkü üye işlev içinde başka bir private işlev çağırılabilir.



}

int main()

{
// DynamicOnly d; Geçersiz!
// DynamicOnly *ptr = new DynamicOnly; Geçersiz!
DynamicOnly *ptr = DynamicOnly::create_object();
//Geçerli çünkü create_object işlevi public bölümde
delete ptr; //Yarat ılan dinamik nesne yok ediliyor.



return 0;
}


Yukarıdaki örneğimizde DynamicOnly sınıfının kurucu işlevi private bölümde bildiriliyor. Böylece global bir işlev içinde DynamicOnly sınıfı türünden bir nesne yaratılması engellenmiş oluyor. Ancak bir üye işlev içinde nesne yaratılabilir. Sınıfın public bölümüne yerleştirilmiş olan create_object isimli
statik üye işlev, DynamicOnly sınıfı türünden bir adresle geri dönüyor.
create_object işlevi new işleci ile yaratılmış bir nesnenin adresi ile geri dönüyor. İşlevimiz üye işlev statüsünde olduğu için, işlevimiz içinde DynamicOnly sınıfının private kurucu işlevinin çağrılması derleme zamanında bir hata oluşumuna neden olmuyor. Şimdi de main işlevine bir göz atalım. main işlevi içinde, statik üye işlev olan create_object işlevi

DynamicOnly::create_object()

biçiminde çağrılabilir değil mi? Böylece işlevin geri dönüş değeri olan dinamik nesnenin adresi ile yaratılan dinamik nesne istenildiği biçimde kullanılabilir.

Revne
11/05/2007, 16:48
Su sitede (http://www.programmersheaven.com/mb/CandCPP/67811/67811/readmessage.aspx)gecen yazıları okuyunca ne sormak istedigini anladım..

When you create a static class variable in C++, you have to define it at file scope. Like so:

class MyClass {
int memberVar;
static int staticMemberVar;
};

int MyClass::staticMemberVar = 0; <-- required

As you've discovered, even if you do forget to define the variable, if you don't write any code that actually uses the variable, you'll get a way with it. As soon as you write code the referenced the variable, you get a linker error.

The exact same thing happens with member functions. If you declare one but forget to define it, you won't link, unless you don't actually ever call it, in which case the linker has no reason to look for it and you get away with it.


Soyle dusunursek eger...
Bir Class birdirimi yaptıgımızda..
class MyClass {
private:
int memberVar;
static int staticMemberVar;
int GetVar();
};


Ve MyClass cls1,cls2,cls3;
seklinde objeler tanımladıgımızda her obje icin ayrı memberVar oluşturulur.. Fakat tum bu objelerin kullandıkları fonksiyon aynıdır.
Daha sonra
int MyClass ::GetVar()
{
......
return 0;
}
seklinde bir bildirim yaparız.. Bu bildirim LINK işelmi yapılırken cağrılır ve tum objeler icin aynı adresteki fonksiyondur.. (Virtual fonksiyonlar burada devereye giriyor.) Burada yapılan tanımlamadır.. Private veya Public erişimleri burada soz konusu deildir..

static bir uye icinde bu durum soz konusudur.. Dolayısıyla
int MyClass::staticMemberVar=0;
seklinde bildirmek gerekiyor. Bu bildrimde LINK işlemi yapılırken kullanılır. ve Tum objeler icin aynı adresteki nesnedir. Burada yapılan tanımlamadır.. Private veya Public erişimleri burada soz konusu deildir..


Ama keşke...

class MyClass {
private:
int memberVar;
static int staticMemberVar=0;
int GetVar();
};

int MyClass ::GetVar()
{
......
return 0;
}
olsaydı... Daha anlaşılır olurdu... :D

Kolay Gelsin....

acehreli
11/05/2007, 21:10
Revne, o daha anlasilir olan kullanim zaten var ama 'integral' (int, long, etc.) ve const turlerde calisir. Daha karmasik turlerde calismamasinin nedeni, herhalde derleyicinin o karmasik nesneyi nerede kuracagini bilememesidir.

O sinif basliginin bir cok derleme biriminde kullanildigini dusun. Hangisi kuracak, kac kere kurulacak, vs. Onun icin bizim secerek bir yerde kurmamiz sorunu cozuyor.

Ali