Professional Documents
Culture Documents
İlerİ Derecede C
İlerİ Derecede C
Genellikle bir sınıfın fiziksel organizasyonu iki dosya halinde yapılır. Sınıfın
ismi X olmak üzere X.h ve X.cpp dosyaları. X.h dosyasının içerisine nesne
yaratmayan bildirim işlemleri yerleştirilir. Yani X.h dosyasının içeriği şunlar
olabilir:
- Sınıf bildirimi
- Sembolik sabit tanımlamaları
- typedef ve enum bildirimleri
- Konuyla ilgili çeşitli global fonksiyonların prototipleri
- inline fonksiyon tanımlamaları
- Global const değişken tanımlamaları
------------------------------------------------------------*/
<Dosya içeriği>
#endif
Bazen özellikle çok küçük sınıflar için ayrı ayrı *.h ve *.cpp dosyaları
oluşturmak yerine bunlar guruplanıp bir kaçı için bir *.h ve *.cpp dosyası
oluşturulabilir. Pek çok geliştirme ortamı (örneğin VisualC) bir sınıf ismi
verildiğinde bu düzenleme işlemini otomatik olarak yapmaktadır. Örneğin
VC6.0’da Insert / NewClass seçildiğinde programcıdan sınıf ismi istenir ve
otomatik şu işlemler yapılır:
Proje geliştirirken disk üzerinde proje için bir dizin oluşturulmalı ve düzenli bir
çalışma sağlanmalıdır. Gurup halinde proje geliştirirken düzenin sağlanması için
çeşitli düzen sağlayıcı programlar kullanılabilmektedir. Projenin dizin yapısı
duruma uygun her hangi bir biçimde seçilebilir. Örneğin, projenin kaynak
kodları SRC alt dizininde, dokümantasyon bilgileri DOC alt dizininde, object
modüller ve çalışabilen programlar BIN alt dizininde, projenin kullandığı
kütüphaneler LIB alt dizininde, deneme kodları SAMPLE alt dizininde
bulunabilir.
Projenin başlık dosyaları bir araya getirilip tek bir başlık dosyası biçimine
dönüştürülebilir. Yani, programcı tek bir başlık dosyası include eder fakat o
başlık dosyasının içerisinde pek çok başlık dosyası include edilmiştir.
Değişkenlerin İsimlendirmesi
- Sınıf isimleri her sözcüğün ilk harfi büyük olacak şekilde ya da tutarlı başka
bir yöntemle belirlenmelidir. C++’da yapılarda bir sınıf olduğu için yapılarla
sınıflar aynı biçimde isimlendirilebilir. Yapıların tamamen C’deki gibi
kullanıldığı durumlarda yapı isimleri her harfi büyük olacak biçimde
belirlenebilir. Bazı kütüphanelerde sınıf isimlerinin başına özel bir karakter de
konulabilmektedir. Örneğin MFC’de sınıf isimlerinin başına ‘C’ getirilmektedir
(CWnd, CBrush, CObject gibi ...).
- Sınıfın veri elemanları üye fonksiyonlar içerisinde kolay teşhis edilsin diye
ayrı bir biçimde isimlendirilmelidir. Genellikle veri elemanları, başına ya da
sonuna ‘_’ konularak ya da ‘d_’, ‘m_’ gibi önekler ile başlatılır.
- Global değişkenler de özel bir biçimde isimlendirilmelidir. Pek çok programcı
global değişkenleri ‘g_’ öneki ile başlatarak isimlendirmektedir.
- Üye fonksiyonlar içerisinde global fonksiyonlar çağırılırken vurgulama için
unary :: operatörü kullanılmalıdır. Örneğin:
::SetData(100);
public:
typedef int SYSID;
public:
Sample();
...
...
private:
void SetItem(SYSID id);
...
...
public:
int m_a;
protected:
int m_b;
private:
int m_c, m_d;
};
int &Sample::GetSetA()
{
return m_a;
}
Sample x;
int y;
x.GetSetA() = 100;
y = x.GetSetA() + 100;
7) Önemli işlevleri olan ana fonksiyonlar: Bu fonksiyonlar sınıf ile ilgili önemli
işlemleri yapan genel fonksiyonlardır.
8) Sanal fonksiyonlar: Çok biçimli (polimorphic) bir sınıf yapısı söz konusuysa
sınıfın bir gurup sanal fonksiyonu olmalıdır.
1- Somut sınıflar: Konu ile ilgili işlemlerin hepsini yapma iddiasında olan,
türetmenin gerekli olmadığı sınıflardır.
2- Soyut sınıflar: Kendisinden türetme yapılmadıkça bir kullanım anlamı
olmayan sınıflardır. C++’da soyut sınıf kavramı saf sanal fonksiyonlarla
syntax’a dahil edilmiştir. Ancak saf sanal fonksiyona sahip olmasa da bu
özellikteki sınıflara da soyut sınıf denir.
3- Ara sınıflar: Türetmenin ara kademelerinde olan sınıflardır.
Bir türetme şeması söz konusuysa herzaman değil ama genellikle soyut sınıflar
en tepede, somut sınıflar en aşağıda, ara sınıflar ise ara kademelerde bulunur.
1- Herhangi bir konuya özgü işlem yapan genel sınıflar: Bu tür sınıflar dış
dünyadaki nesnelere karşılık gelen genel sınıflardır.
2- Yararlı sınıflar (utility class): Bunlar her türlü özel konulara ilişkin olmayan,
her türlü projede kullanabileceğimiz genel sınıflardır. Örneğin, string işlemlerini
yapan sınıflar, dosya işlemlerini yapan sınıflar, tarih işlemlerini yapan sınıflar
gibi.
3- Nesne tutan sınıflar (container class / collection class): Dizi, bağlı liste,
kuyruk, ikili ağaç, hash tabloları gibi veri yapılarını kurup çalıştıran, amacı bir
algoritmaya göre birden çok nesne tutmak olan sınıflardır. 1996 yılında STL
denilen template tabanlı kütüphane C++ programlama diline dahil edilmiştir ve
C++’ın standart kütüphanesi yapılmıştır. STL içerisinde pek çok yararlı sınıf ve
nesne tutan sınıf standart olarak vardır.
4- Arabirim sınıflar (interface class): Sisteme, donanıma ya da belli bir duruma
özgü işlemler için kullanılan sınıflardır. Bu tür özel durum üzerinde işlem
yapmak için ayrı sınıflar tasarlamak iyi bir yaklaşımdır. Böylece sisteme ya da
donanıma özgü durumlar arabirim sınıflar tarafından ele alınabilir. Bu durumlar
değiştiğinde diğer sınıflar çalışmadan etkilenmez, değişiklik sadece arabirim
sınıflar üzerinde yapılır.
Her ne kadar proje geliştirme işleminin teorik tarafı bu adımları sırası ile içerse
de küçük gruplar ya da tek kişilik çalışmalarda programcı kendi sezgisiyle
bunları eş zamanlı olarak sağlamaya çalışabilir. Teorik açıklamalar ancak genel
kurallardır. Bu genel kurallar izlendiği halde başarısız olunabilir, izlenmediği
halde başarılı olunabilir. Nesne yönelimli teknik kullanılan projelerde analiz
aşamasından sonra proje için gerekli sınıfların tespit edilmesi ve aralarındaki
ilişki açıklanmalıdır. Eğer böyle yapılırsa bundan sonra projenin kodlama
aşamasında problemleri azalır. Proje içerisindeki sınıfların tespit edilmesi,
bunların arasındaki ilişkilerin belirlenmesi sürecine nesne yönelimli modelleme
denilmektedir.
Sınıf nesneleri büyükse eleman olan sınıf nesnelerinin heap üzerinde tahsis edilmesi daha uygun
olabilir. Bu durumda elemana ilişkin sınıf türünden bir gösterici veri elemanı alınır, sınıfın
başlangıç fonksiyonu içerisinde bu göstericiye tahsisat yapılır, bitiş fonksiyonu içinde de geri
bırakılır. Örneğin:
class B {
private:
A *m_pA;
//...
};
Bu biçimde bir kullanma ile diğerinin arasında kavramsal bir farklılık yoktur.
Her iki durumda da eleman olan nesnenin ömürleri elemana sahip sınıfın
ömrüyle aynıdır.
3- Başka bir sınıf nesnesinin adresini alarak veri elemanı biçiminde kullanma
(aggregation): Bu durumda kullanılacak nesne kullanan nesneden daha önce
yaratılmıştır, belki daha sonra da var olmaya devam edecektir. Sınıfın yine nesne
adresini tutan bir gösterici veri elemanı vardır. Kullanılacak nesnenin adresi
kullanan sınıfın başlangıç fonksiyonu içerisinde alınarak veri elemanına atanır.
Yani bu durumda kullananılacak nesne kullanan sınıf tarafından yaratılmamıştır.
Bu durum genellikle sınıf ilişki diyagramlarında içi boş yuvarlak (ο) ya da karo
(◊) ile gösterilir.
class B {
public:
B (A *pA)
{
m_pA = pA;
}
private:
A *m_pA;
//...
};
A a;
{
B b(&a);
...
}
...
Bu tür kullanma durumu genellikle bir nesnenin başka bir nesneyle ilişkili işlemler yaptığı
durumlarda, ancak kullanılan nesnenin bağımsız olarak kullanılmasına devam ettiği durumlarda
tercih edilir. Örneğin, bir bankada bir müşterinin hesabı üzerinde işlem yapmak için kullanılan
bir sınıf olsun. Burada müşteri nesnesi daha önce yaratılmalıdır, belki üzerinde başka işlemler de
uygulanmıştır, ancak hesap işlemleri söz konusu olduğunda o nesne başka bir sınıf tarafından
kullanılacaktır. Gösterici yoluyla kullanma söz konusu olduğundan nesnedeki değişiklik
kullanan sınıf tarafından hemen fark edilir.
4- Üye fonksiyon içerisinde kullanma (association): Bu durumda sınıfın bir üye
fonksiyonu başka bir sınıf türünden gösterici parametresine sahiptir, yani sınıf
başka bir sınıfı kısmen kullanıyordur. Bu durum genellikle sınıf ilişki
diyagramlarında kesikli oklarla gösterilir.
A
class B {
public:
void Func(A *pA);
//...
};
Bunların dışında bir sınıf başka bir sınıfı sınıfın yerel bloğu içerisinde kullanıyor
olabilir. Ancak bu durum önemsiz bir durumdur, çünkü bu kullanma ilişkisi
kimseyi ilgilendirmeyecek düzeydedir.
String Sınıfları
Yazılarla işlemler yaparken klasik olarak char türden diziler kullanılır. Ancak
dizilerin uzunluğu derleme zamanında sabit ifadesiyle belirtilmek zorundadır.
Bu durum yazılar üzerinde ekleme ve çıkarma işlemleri yapıldığında bellek
verimini düşürmekte ve programı karmaşık hale getirmektedir. Bellek
kayıplarını engellemek için dizi dinamik tahsis edilebilir, bu durumda dizi
büyütüleceği zaman yeterli uzunlukta yeni bir blok tahsis edilebilir. Ancak
dinamik tahsisatlar programcıya ek yükler getirmektedir. İşte bu nedenlerden
dolayı yazı işlemlerinin bir sınıf tarafından temsil edilmesi (yani encapsule
edilmesi) çok sık rastlanılan bir çözümdür.
Bir string sınıfının veri elemanları ne olmalıdır? Yazı için alan dinamik olarak
tahsis edileceğine göre dinamik alanı tutan char türden bir gösterici olmalıdır.
Yazının uzunluğunun tutulmasına gerek olmasa da pek çok işlemde hız kazancı
sağladığından uzunluk da tutulmalıdır. Profesyönel uygulamalarda yazı için blok
tam yazı uzunluğu kadar değil, daha büyük alınır. Böylece küçük ekleme
işlemlerinde gereksiz tahsisat işlemleri engellenir. Tabii, yazı uzunluğunun yanı
sıra tahsis edilen bloğun uzunluğu da tutulmalıdır. Bloklar önceden belirlenmiş
bir sayının katları biçiminde tahsis edilebilir. Bu durumda string sınıfının tipik
veri elemanları şöyle olacaktır:
class CString {
//...
protected:
char *m_pStr;
unsigned m_size;
unsigned m_length;
static unsigned m_allocSize;
};
Sınıfın m_allocSize isimli static veri elemanı hangi blok katlarında tahsisat
yapılacağını belirtir. Bu static veri elemanı başlangıçta 10 gibi bir değerdedir.
Yani, bu durumda blok 10’un katları biçiminde tahsis edilir. Bu durumda bir
CString sınıf nesnesinin yaratılmasıyla şöyle bir durum oluşacaktır:
ankara\0
m_pStr
m_size
m_length
Sınıf çalışması olarak tasarlanan string sınıfı MFC CString sınıfına çok
benzetilmiştir. Sınıfın başlangıç fonksiyonları şunlardır:
CString();
CString(const CString &a);
CString(char ch, unsigned repeat = 1);
CString(const char *pStr);
CString(const char *pStr, unsigned length);
~CString();
Sınıf çalışmasındaki CString sınıfının pek çok türdeki üye fonksiyonu vardır.
Bu fonksiyonlar şu işleri yaparlar:
drive = path.Left(2);
Görüldüğü gibi bu fonksiyonlar geri dönüş değeri olarak geçici bir nesne
yaratmaktadır. Tabi, CString sınıfının bir atama operatör fonksiyonu
olmalıdır. drive = path.Left(2); işleminde şunlar yapılır:
a- Fonksiyon içerisinde soldaki iki karakter bir CString nesnesi olarak elde
edilir ve bu nesne ile return edilir.
b- Geçici nesne kopya başlangıç fonksiyonu ile yaratılır.
c- Geçici nesneden drive nesnesine atama için atama operatör fonksiyonu
çağırılır.
d- Geçici nesne için bitiş fonksiyonu çağırılır.
- CString Mid(int first) const;
CString Mid(int first, int count) const;
Bu fonksiyonlar yazının belli bir karakter index’inden başlayarak n tane
karakterini alıp yeni bir CString nesnesi olarak verir. Fonksiyonun tek
parametreli biçimi geri kalan yazının tamamını almaktadır.
- void MakeUpper();
void MakeLower();
Sınıf içerisinde tutulan yazıyı büyük harfe ve küçük harfe dönüştürür.
- void Format(PCSTR pStr, ...);
Bu fonksiyon değişken sayıda parametre alan bir fonksiyondur. sprintf()
gibi çalışır, sınıfın tuttuğu eski yazıyı silerek yeni yazıyı oluşturur.
- void MakeReverse();
Sınıfın tuttuğu yazıyı tersdüz eder.
- void TrimLeft();
void TrimRight();
Yazının solundaki ve sağındaki boşlukları atar.
- int Find(char ch) const;
int Find(PCSTR pStr) const;
Bu fonksiyonlar yazı içerisinde bir karakteri ve bir yazıyı ararlar, geri dönüş
değerleri başarılıysa bulunan yerin yazıdaki index numarası, başarısızsa –1
değeridir. Sınıfın ReverseFind() fonksiyonu aramayı tersten yapar.
- Sınıfın [] operatör fonksiyonu sanki diziymiş gibi yazının bir indexine erişir.
[] operatör fonksiyonu hem sol taraf hem de sağ taraf değeri olarak
kullanılabilir. Örneğin:
CString s = “Ankara”;
s[0] = ‘a’; // s.operator[](0) = ‘a’;
Date x;
...
(int) x;
int a;
Date b;
...
a = b;
int a, b;
Date c;
a = b + c; // a = b + c.operator int();
1) c = a.operator double() + b;
2) c = a + Complex(b);
Her iki biçim de mümkünse iki anlamlılık hatası oluşur. Eğer yalnızca bir
durum sağlanıyorsa işlem normal olarak yapılır. Her iki operandın da
diğerinin türüne dönüştürülebildiği durumlarda iki anlamlılık hatalarından
kurtulmak için ifade açıkça yazılabilir. Yani,
c = a + Complex(b);
c = (double) a + b;
işlemi error ile sonuçlanır. Eğer bu işlem mümkün olsaydı biz CString
nesnesinin kullandığı dinamik alan üzerinde değişiklik yapabilirdik ve sınıfın
veri elemanı bütünlüğünü bozabilirdik. puts(s), strlen(s), strcpy(buf, s)
gibi işlemler mümkündür, ancak strupr(s), strcpy(s, buf) gibi işlemler
error ile sonuçlanır.
pUpdate = s.GetBuffer(30);
s.ReleaseBuffer(-1);
c = a + b;
puts(c);
a += b;
c = a + “istanbul”;
a += ‘x’;
- CString sınıfının başka bir CString nesnesiyle, bir yazı ile her türlü
karşılaştırmayı yapan bir grup üye ve global operatör fonksiyonu vardır.
- CString sınıfını başka CString nesnesine atamakta kullanılan ve bir
karakter atamakta kullanılan atama operatör fonksiyonları vardır.
- Nihayet sınıfın cout ve cin nesneleriyle işlem yapabilecek << ve >>
operatör fonksiyonları vardır.
Anahtar Notlar: Bir sınıf için kopya başlangıç fonksiyonu gerekiyorsa atama
operatör fonksiyonu da gerekir. Kopya başlangıç fonksiyonu ve atama operatör
fonksiyonunun gerektiği tipik durumlar başlangıç fonksiyonlarına veri
elemanları için dinamik tahsisat yapıldığı durumlardır. Atama operatör
fonksiyonlarının hemen başında nesnenin kendi kendine atanıp atanmadığı tespit
edilmelidir.
Anımsatma: Bir sınıf nesnesi aynı türden geçici bir nesneyle ilk değer
verilerek yaratılıyorsa normal olarak işlemlerin şu sırada yapılması beklenir:
Anahtar Notlar: İyi bir nesne yönelimli teknikte az sayıda global değişken
kullanılmalıdır. Global değişkenler bir sınıf ile ilişkilendirilip sınıfın static veri
elemanı yapılmalıdır.
Anahtar Notlar: İyi bir nesne yönelimli teknikte sembolik sabitler için mümkün
olduğu kadar az #define kullanılır. Bunun yerine sembolik sabitler bir sınıf
içinde enum olarak bildirilir, böylece global faaliyet alanı kirletilmemiş olur.
#ifndef _SHELL_H_
#define _SHELL_H_
class Shell {
public:
Shell(const char *pPrompt):m_prompt(pPrompt){}
void Run();
private:
CString m_prompt;
CString m_command;
CString m_param;
static char *m_cmd[];
};
#endif
/* shell.cpp */
#include <stdio.h>
#include <stdlib.h>
#include <iostream.h>
#include "general.h"
#include "cstring.h"
#include "shell.h"
void Shell::Run()
{
char buf[LINELEN];
CString cmd;
for (; {
cout << m_prompt << '>';
gets(buf);
cmd = buf;
cmd.TrimLeft();
int cmdIndex = cmd.FindOneOf(" \t\0");
m_command = cmd.Left(cmdIndex);
m_param = cmd.Mid(cmdIndex);
m_param.TrimLeft();
for (int i = 0; cmdProc[i].pCommand != NULL; ++i)
if(cmdProc[i].pCommand == m_command) {
cmdProc[i].pProc(this);
break;
}
if (cmdProc[i].pCommand == NULL) {
cout << "Bad command or file name\n";
continue;
}
}
}
int main()
{
Shell shell("CSD");
shell.Run();
return 0;
}
Satır satır işlem yapılan editörlere line editör denir. DOS’un edlin editörü,
UNIX’in ed editörü tipik line editörlerdir. Line editörlerde daha önce
uygulamasını yaptığımız bir komut satırı vardır, kullanıcı bir komut ve bir satır
numarası girer, editör o satır üzerinde ilgili işlemleri yapar. Böyle bir line editör
uygulaması için bir Editor sınıfı ve bir Shell sınıfı alınabilir. Shell
sınıfı Editor sınıfının bir veri elemanı gibi kullanılabilir. Bu sınıfların yanı
sıra işlemleri kolaylaştıran String ve File sınıflarından da faydalanılabilir.
Dosya işlemleri de tipik olarak sınıflarla temsil edilebilir. Pek çok sınıf
kütüphanesinde dosya işlemlerini yapan bir sınıf vardır. Java, C# gibi nesne
yönelimli dillerde dosya işlemleri için tek bir sınıf değil polimorfik özelliği olan
bir sınıf sistemi kullanılmaktadır. Ayrıca C++’ın standart kütüphanesinde dosya
işlemleri iostream sınıf sistemi ile de yapılabilmektedir. Maalesef bu sınıf
sistemi dosya işlemleri için yetersiz kalmaktadır. Bu nedenle dosya işlemleri
için iostream sınıf sistemini kullanmak yerine çoğu kez bu işlem için ayrı
bir sınıf tasarlama yoluna gidilmektedir. MFC kütüphanesinde dosya işlemleri
için CFile isimli bir sınıf tasarlanmıştır. CFile sınıfı binary dosyalar
üzerinde işlemler yapar, CFile sınıfından CStdioFile isimli bir sınıf
türetilmiştir, bu sınıf da text dosyaları üzerinde işlem yapmaktadır. Örnek bir
dosya sınıfı için MFC kütüphanesindeki CFile sınıfına benzer bir sınıf
tasarlanacaktır.
Anahtar Notlar: Bir sınıf başka bir sınıfı kullanıyor olsun. Örneğin B sınıfının
A sınıfını kullandığını düşünelim. B sınıfı ve A sınıfı ikişer dosya halinde
yazılmış olsun. A.h dosyası B sınıfının hangi dosyasında include edilmelidir?
Bu sorunun yanıtı, biz dışarıdan B sınıfını kullanırken yalnızca B.h dosyasının
include edilmesinin probleme yol açıp açmayacağıyla verilir. Yani, eğer A sınıfı
B.h içerisinde kullanılmışsa, yani B sınıfının bildirimi içerisinde kullanılmışsa,
sınıfın B.h içerisinde include edilmesi gerekir (genellikle bu durum
composition ya da aggregation durumudur). Eğer A sınıfı yalnızca B sınıfının
üye fonksiyonları içerisinde kullanılmışsa bu durumda A sınıfı yalnızca B.cpp
içerisinde kullanılmıştır, dolayısıyla B.cpp’nin içerisinden include edilmesi
uygundur. Çok temel olan dosyaların include edilmesi sırasında ek bir include
koruması uygulanabilir ya da çok temel dosyalar tamamen uygulama
programcısı tarafından zorunlu olarak include edilmesi gereken bir durum
biçiminde ele alınabilir.
Anahtar Notlar: Bir sınıfın içerisinde bir enum bildirimi yapılmışsa enum
sabitleri o sınıfın içerisinde doğrudan kullanılabilir, ancak dışarıdan doğrudan
kullanılamaz. Ancak enum sınıfın public bölümündeyse çözünürlük
operatörüyle dışarıdan kullanılabilir. Büyük projelerde global alandaki isim
çakışmasını engellemek için sembolik sabitlerin #define ile oluşturulması
tavsiye edilmez, bunun yerine sembolik sabitler bir sınıfla ilişkilendirilmeli ve o
sınıfın enum sabiti olarak bildirilmelidir.
Default başlangıç fonksiyonu ile nesne yaratılırsa dosya daha sonra sınıfın
CFile::Open() üye fonksiyonuyla açılmalıdır. İkinci başlangıç fonksiyonu
daha önce fopen() fonksiyonu ile açılmış olan dosyayı sınıf ile ilişkilendirilip
kullanmak için düşünülmüştür. Nihayet üçüncü fonksiyon, ismi ile belirtilen
dosyayı belirtilen modda açar.
- Sınıfın bitiş fonksiyonu sınıf tarafından açılıp kullanılmakta olan bir dosya
varsa onu kapatır, yoksa bir şey yapmaz.
Anahtar Notlar: Pek çok sınıf için bitiş fonksiyonu koşulsuz bir geri alma
işlemini yapmaz. Önce bir kontrol yapar, bu kontrolle başlangıç işlemlerinin
yapılıp yapılmadığını anlar, yapılmışsa onu geri alır, yapılmamışsa hiç bir şey
yapmaz. Örneğin CFile gibi bir sınıfın bitiş fonksiyonu hemen dosyayı
kapatmaya yeltenmemelidir. Önce dosyanın açılıp açılmamış olduğuna bakmalı
açılmışsa kapatmalıdır. Bu işlem genellikle sınıfın veri elemanına özel bir
değerin yerleştirilmesi ve bunun kontrol edilmesi ile yapılmaktadır. Örneğin:
CFile::CFile()
{
m_f = NULL;
}
CFile::~CFile()
{
if (m_f)
::flose(m_t);
}
Dosya açış modlarına ilişkin enum sabitleri bütün bitleri 0, yalnızca bir biti 1
olan sayılardır.
Close() fonksiyonunun tasarımında dosya açıksa kapatılmıştır, zaten sınıfın bitiş fonksiyonu
da bu fonksiyonu çağırmaktadır.
Anahtar Notlar: Sınıfın bazı üye fonksiyonları kısmen aynı şeyleri yapıyor
olabilir. Bu durumda bu aynı işlemler bir üye fonksiyona yaptırılır, bu üye
fonksiyon dışarıdan çağırılmayacağına göre sınıfın private bölümüne
yerleştirilir.
1- Başarısızlık durumda hiç bir şey yapılmaz, bir mesaj verilir görmezlikten
gelinir.
2- Başarısızlık başlangıç fonksiyonu içerisinde tespit edilir ve bir exception
oluşturulur. Programcı da nesneyi try bloğunda yaratır. Örneğin, MFC’de
dosya CFile sınıfının başlangıç fonksiyonunda açılamadıysa
CFileException sınıfı türünden dinamik bir nesne yaratılıp o adres ile
throw edilir. Örneğin,
try {
CFile f(...);
//...
}
catch (CFileException *pFileException) {
//...
}
if (!f) {
cout << “Cannot open file...\n”;
exit(EXIT_FAILURE);
}
class CFile {
//...
private:
BOOL m_bSuccess;
};
CFile::CFile(....)
{
if (Dosya açılamadıysa)
m_bSuccess = FALSE;
//...
}
BOOL CFile: perator !() const
{
return !m_bSuccess;
}
zeet06
Açık Profil bilgileri
28.11.08, 15:03 #2
zeet06
Vefakar Üye
Copy(&fs, &fd);
/* or */
class A {
//...
};
Burada derleyici önce list sınıfını int türü ile açar, Queue
sınıfının template parametresinin türü int türüne açılmış list
olur. Yani bu örnekte her elemanı bağlı liste olan bir kuyruk sınıfı
oluşturulmuştur. Bağlı listede int türden bilgiler tutulmaktadır
(burada ifadenin shift operatörü (>>) ile karışmaması için araya
boşluk bırakılması gerekir).
Template sınıf ile aynı isimli normal bir sınıf olabilir ama normal
sınıfın template açılım türü belirtilmelidir. Örneğin:
template <class T>
class Sample {
//...
};
namespace Y {
void X::Func() /* error */
{
}
}
//...
namespace X {
int b;
//...
}
using Bildirimi
Örneğin:
using namespace std;
Örneğin:
using std::cout;
class B : public A {
public:
using A::m_a;
//...
};
ile
string str;
aynı anlamdadır.
return false;
}
trkstring a(“ılgaz”);
trkstring b(“ismail”);
if (a < b) {
//...
}
Başlangıç Fonksiyonları:
5- basic_string(const E *str);
Parametresiyle belirtilen adresten ‘\0’ görene kadarki kısımdan
yazıyı oluşturur. En çok kullanılan constructor’dur. Örneğin:
string a(“ankara”);
6- basic_string(size_type n, E ch);
Aynı karakterden n tane olan bir nesne oluşturur. Örneğin:
assert(a == b);
assert(a == b);
if (s == k) {
//...
}
if (s < “samsun”) {
//...
}
index = s.find(“kara”);
if (index == string::npos) {
cerr << "error olustu" << endl;
exit(1);
}
cout << index << endl;
index = s.find(“kara”);
if (index == string::npos) {
cerr << "error olustu" << endl;
exit(1);
}
string t;
t = s.substr(index, 2);
string s;
getline(cin, s);
typedef T *pointer;
typedef const T *const_pointer;
typedef T &reference;
typedef const T &const_reference;
Algoritma Analizi
O(1) sabit
O(log n) logaritmik
O(n) doğrusal
O(n log n) doğrusal logaritmik
O( ) karesel
O( ) küpsel
O( ) üstel
STL’de temel veri yapıları ile nesne tutan pek çok sınıf vardır.
Bir veri yapısının nerelerde kullanılacağını bilmek gerekir. STL
içerisinde bu sınıflar hazırdır, ancak programlama sırasında bu
kararın verilmesi tamamen programcıya bağlıdır. Bir programda
hangi nesne tutan sınıfı kullanacağımız uygulamamıza ve o nesne
tutan sınıfın algoritmik yapısına bağlıdır.
1- list();
Default başlangıç fonksiyonu ile başlangıçta boş bir bağlı liste
yaratılır.
2- list(const list &r);
Kopya başlangıç fonksiyonudur.
3- explicit list(size_t n, const T &value =
T());
Bu başlangıç fonksiyonu T template parametresi, yani bağlı
listede saklanacak nesnelerin türü olmak üzere n tane bağlı liste
elemanı oluşturur. n eleman da T normal türlere ilişkinse 0, sınıf
türündense default başlangıç fonksiyonuyla doldurulur.
Örnek:
list<int> x(10);
assert(x.size() == 10);
Burada int türünden iki ayrı bağlı liste vardır. Önce soldaki liste
boşaltılır, sonra sağdaki listenin elemanlarından yeni bir bağlı
liste yapılır. Her iki bağlı listenin elemanlarında da aynı değerler
vardır ama elemanlar gerçekte farklıdır.
Bir bağlı liste için başa ya da sona eleman ekleme, araya eleman
insert etme, herhangi bir elemanı silme en çok kullanılan
işlemlerdir. Bu işlemleri yapan fonksiyon isimleri diğer nesne
tutan sınıflar için de ortak isimlerdir. Genel olarak bütün nesne
tutan sınıflar için (bazı istisnaları vardır) front() ve back()
isimli fonksiyonlar ilk ve son elemanı almak için (bunları
silmezler), push_front() ve push_back() isimli
fonksiyonlar başa ve sona eleman eklemek için, pop_front()
ve pop_back() isimli fonksiyonlar baştaki ve sondaki
elemanları silmek için (silinen elemanları vermezler),
insert() isimli fonksiyonlar araya eleman eklemek için ve
remove() isimli fonksiyonlar eleman silmek için kullanılırlar.
Anahtar Notlar: Bir referans geçici bir nesne ile ilk değer
verilerek yaratılıyorsa (geçici nesneyi derleyici de oluşturabilir)
referansın const olması gerekir. Referans C++’ın doğal
türlerindense verilen ilk değer referans ile aynı türden bir nesne
değilse ya da sabitse derleyici geçici nesne oluşturacağından yine
referansın const olması gerekir.
STL’de herbir nesne tutan sınıfın içerisinde iteratör diye bir tür
ismi vardır. Bu tür ismi ya doğrudan template parametresi
türünden gösterici typedef ismidir ya da o sınıf içerisinde
tanımlanmış bir sınıfın ismidir.
Eğer iteratör ismi bir gösterici ise X nesne tutan sınıf olmak
üzere şöyle bir bildirim uygulanmıştır:
template <class T, ...>
class X {
public:
typedef T *iterator;
//...
};
zeet06
Açık Profil bilgileri
28.11.08,
#3
15:05
zeet06
Vefakar Üye
reserve() Fonksiyonu
reserve() işleminden sonra elde edilen alan bir dizi gibi kullanılıp []
ile erişim sağlanabilir. Tabii henüz size = 0 olduğundan [] ile erişim
size değerini güncellemeyecektir. size değeri yalnızca push_back(),
insert() ve erase() işlemlerinden etkilenir. Örneğin insert() ve
erase() işlemlerinde sınıf size değerini göz önüne alarak kaydırma
yapacaktır. end() fonksiyonu ile verilen iterator şüphesiz string
sınıfında olduğu gibi capacity değil size ile ilgilidir.
Sınıf Çalışması: Bir bağlı liste bir de vector nesnesi tanımlayınız. Bağlı
listeye 1 ile 100 arasında rastgele 20 eleman ekleyiniz. Bağlı listedeki
elemanları vector nesnesine kopyalayınız. vector nesnesini sort()
fonksiyonu ile sort ediniz. sort edilmiş vectorü yeniden bağlı listeye
kopyalayınız ve bağlı listedeki elemanları yazdırınız.
Cevap:
/* vector_sort.cpp */
#include <iostream>
#include <stdlib.h>
#include <list>
#include <vector>
#include <algorithm>
int main()
{
list<int> list;
vector<int> vec;
vec.reserve(list.size());
vec.resize(list.size());
copy(list.begin(), list.end(), vec.begin());
sort(vec.begin(), vec.end());
copy(vec.begin(), vec.end(), list.begin());
system("PAUSE");
return 0;
}
1- void erase(iter);
2- void erase(iterfirst, iterlast);
Cevap:
/* vector_queue.cpp */
#include <iostream>
#include <vector>
#include <algorithm>
void main()
{
int val;
Queue<int> q(10);
q.Disp();
Java ya da C#
C++
Sample s;
s = new Sample();
Object o;
o = s;
o.ToString();
Sample *s;
s = new Sample();
Object *o;
o = s;
o->ToString();
Bu dillerde sınıf ve dizi dışındaki tüm türler normal bir biçimde değerle
aktarılmaktadır. Java ve C#’daki nesne tutan sınıfların hepsi Object
türünden nesneleri tutar, yani C++’a göre aslında Object sınıfı türünden
göstericilerden oluşmaktadır. Bu dillerde nesne tutan sınıf içerisine eleman
eklemek çok kolay bir biçimde yapılabilmektedir. Ancak tüm nesne tutan
sınıflar Object sınıfına ilişkin olduğu için sınıfı dolaşıp sanal
fonksiyonları çağırabilmek için önce bir aşağıya dönüşüm uygulamak
gerekebilir. Örneğin Triangle Shape sınıfından türetilmiş bir sınıf
olsun, Shape sınıfı da default olarak Object sınıfından türetilmiş olsun.
Triangle t = new Triangle();
list.Add(t);
Shape s = (Shape) list.Get();
Java’da int, long gibi doğal türler bir sınıf değildir, bu türleri nesne
tutan sınıflarda saklayabilmek için çeşitli sarma sınıflar kullanılır.
Örneğin:
list.Add(new Integer(i));
Heterojen şekillerin C’de saklanabilmesi için neredeyse tek yol bir tür
bilgisini de şekil ile birlikte saklamak olabilir. Yani örneğin aşağıdaki gibi
Shape türünden bir bağlı liste oluşturulabilir.
Shape
Shape
Circle
Triangle
Uygulamanın kendisini App isimli bir sınıf ile temsil edersek bu bağlı
listenin global olmak yerine bu sınıfın bir veri elemanı olması daha uygun
olur.
Bir şekil çizildiği zaman şekle ilişkin bir sınıf nesnesi dinamik olarak
yaratılır ve bağlı listeye eklenir. Böylece bağlı liste heterojen nesnelerin
adreslerini tutan bir biçime getirilmiş olur. Fareyle click yapıldığında
hangi şekle click yapıldığının anlaşılması için bağlı listenin dolaşılarak
Shape sınıfının IsInside() sanal fonksiyonu çağırılır. Her şekil
sınıfının IsInside() isimli fonksiyonu bir noktanın kendisinin
içerisinde olup olmadığını tespit etmek amacıyla yazılmalıdır.
virtual bool IsInside(int x, int y) const = 0;
Tetris oyun programı tipik olarak çokbiçimlilik özelliği yoğun bir biçimde
kullanılarak tasarlanabilir. Oyunun tasarımı için önce oyundaki elemanları
sınıflarla temsil etmek gerekir (transformation). Örneğin düşen şekiller,
puanlama mekanizması, ekran işlemleri, uygulamanın kendisi birer sınıfla
temsil edilebilir. Şekillerin düşmesi ve hareket etmesi tipik olarak
çokbiçimli bir mekanizma ile sağlanabilir. Şöyleki, şekiller örneğin
Shape gibi bir sınıftan türetilmiş sınıflarla temsil edilir. Shape sınıfının
sola döndürme, sağa döndürme, sola hareket etme, sağa hareket etme ve
aşağıya doğru hareket etme fonksiyonları olur. Bütün bu fonksiyonlar
sanal ya da saf sanal alınabilir. Şekillerin hareketleri tamamen türden
bağımsız olarak Shape sınıfı ile temsil edilir. Algoritmalar belirli bir
şeklin hareket etmesine göre değil genel bir şeklin hareket etmesine göre
düzenlenir. Her şekil kendi hareketini sanallık mekanizması içinde
yapacaktır. Örneğin,
Shape *pShape = createNewShape();
for (; {
sleep(50);
pShape->MoveDown();
//...
}
stack sınıfı adaptör bir sınıftır, yani başka bir STL sınıfından
faydalanılarak yazılmıştır (yani stack sınıfı yazılırken başka bir sınıf
veri elemanı biçiminde kullanılarak (composition) yazılmıştır). Ancak
stack sınıfında kendisinden faydalanılan sınıf bir template parametresi
yapılmıştır, yani değiştirilebilir.
RemoveChar
Remove block
InsertChar
InsertBlock
....
1- Her sınıf undo işlemlerine ilişkin gerekli bilgileri kendi içerisinde tutar.
Örneğin RemoveChar sınıfı silinen karakteri ve onun editördeki yerini
tutar. InsertBlock insert edilen bloğun yerini tutabilir.
2- Her sınıf geri alma işlemini kendine özgü biçimde, yani çokbiçimli
olarak yapar. Undo işlemi stack’in tepesinden bilgiyi çekip TakeBack()
sanal fonksiyonunun çağırılmasıyla yapılır.
3- Undo veri yapısı Operation * türünden bir stack sisteminde tutulur ve
TakeBack() sanal fonksiyonu aşağıdaki gibi çağırılır:
Func(X());
Burada derleyici template fonksiyonu açarken T türünü X sınıfı olarak alır.
Dolayısıyla fonksiyonun derlenebilmesi için sınıfın fonksiyon çağırma
operatör fonksiyonunun yazılmış olması gerekir.
Bir sınıfın farklı parametrik yapıya sahip birden fazla fonksiyon çağırma
operatör fonksiyonu olabilir. Bu operatör fonkisyonlarının geridönüş
değerleri herhangi bir biçimde olabilir. Fonksiyon çağırma operatör
fonksiyonu şu biçimde çağırılabilir:
class X {
//...
void operator()(int a);
void operator()(int a, int b);
//...
};
X a;
a(10,20);
a.operator()(10,20);
C++’da karmaşık pek çok durum için faaliyet alanı kavramı yetersiz
kalmıştır, bu yüzden isim arama (name lookup) kavramı geliştirilmiştir.
Derleyici bir isimle karşılaştığında onu sırasıyla nerelerde arayacaktır?
İsim arama işleminde arama aşama aşama yapılır, isim bir yerde
bulunduğunda arama kesilir. İsim hiçbir yerde bulunamazsa bu durum
error oluşturur.
C++ derleyicisi önce ismi isim arama özelliğine göre arar, bulursa
bulduktan sonra erişim kontrolüne bakar, daha sonra da kullanım kontrolü
uygulayarak ifadenin geçerli olup olmadığına bakar.Yani önce isim
bulunmakta sonra erişime bakılmaktadır.
//...
A();
B b;
C c;
};
try {
A a;
}
catch (...) {
}
Exception Specification
void Func()
{
try {
//...
}
catch(X) {
throw;
}
catch(Y) {
throw;
}
catch(...) {
std::unexpected();
}
}
void Func()
{
try {
//...
}
catch(...) {
std::unexpected();
}
}
temp = ifade;
param = temp;
catch (int a)
{
//...
}
throw X();
catch (X a)
{
//...
}
Bu durum etkin bir yöntem değildir. Çünkü bir sınıf türü ile throw
edildiğinde geçici bölge ve catch parametreleri için başlangıç ve bitiş
fonksiyonları çağırılacaktır.
throw X();
catch (X &a)
{
//...
}
catch (X *pX)
{
//...
}
Eğer tahsisat heap üzerinde yapılmışsa throw işlemi için tahsis edilen
alanın boşaltılması catch bloğunu düzenleyen programcı tarafından
yapılmalıdır. Örneğin:
throw new X();
catch(X *pX)
{
//...
delete pX;
}
Burada MFC için özel bir durum söz konusudur. MFC kütüphanesindeki
fonksiyonlar bazen global nesneleri adresleriyle de throw edebilmektedir.
Bu nedenle exception nesnesinin silinmesi delete operatörü ile değil
CException sınıfının Delete() fonksiyonu ile yapılmaktadır.
Delete() fonksiyonu adresin heap üzerinde tahsis edilip edilmediğine
bakar. Edilmişse delete operatörü ile nesneyi siler. Global bir tahsisat söz
konusuysa nesneyi silmez.
İfadesiz throw işlemleri genellikle akış bakımından catch bloklarının içerisinde yapılır.
Bu işlemlerde amaç exception durumunu kısmi olarak ele alıp sanki hiç ele alınmamış
gibi bir dışardaki catch bloğuna atmaktır. Örneğin:
try {
Func();
}
catch (X *pX)
{
//...
}
Func()
{
try {
Sample();
}
catch (X *pX)
{
//...
throw;
}
}
Son yıllarda basit nesne yönelimli dillere olan gereksinim artmıştır. Java
Sun firması tarafından basit bir nesne yönelimli programlama dili olarak
tasarlanmıştır. C# javanın biraz daha iyileştirilmiş biraz daha C++’a
yaklaştırılmış ve Microsoft teknolojileriyle entegre edilmiş biçimidir. Java
ve C# dillerinin tasarımındaki ikinci büyük kavram çalışabilen kodun
taşınabilirliği (binary portibility) yani Java ve C# derleyicilerinin ürettiği
kod o anda çalıştığımız makinanın işlemcisinin makina komutları değildir.
Aslında hiç bir işlemcinin makina komutları değildir, bir arakoddur. Java
derleyicilerinin çıktısı *.class, C# derleyicilerinin çıktısı *.exe
biçimindedir. Bu arakoda Java terminolojisinde “byte code”, .NET
terminolojisinde “Microsoft Intermediate Language” denir. C#’ın ürettiği
*.exe kodu normal bir *.exe kodu değildir. Byte code ve MIL kodları
başka bir programdan faydalanılarak çalıştırılmaktadır. Örneğin bir java
programının derlenip çalıştılırması için şöyle yapılmaktadır:
javac x.java
java x.class
- Önişlemci
- Göstericiler
- Operatör fonksiyonları
- Template işlemleri
- Default parametre kavramı
- Çoklu türetme
- Yerel sınıf nesneleri tanımlama
Diğer küçük özellikler
zeet06
Açık Profil bilgileri
28.11.08, 15:07 #4
zeet06
Vefakar Üye
void Sample::Func()
{
(this->*m_pf)();
//...
}
class Y : public X {
public:
void FuncY();
};
void (X::*pX)();
pX = &Y::FuncY; //error
X x;
(x.*pX)();
Örnek 1:
#include <iostream>
#include <functional>
#include <algorithm>
#include <list>
class A {
public:
virtual bool Func() = 0;
};
class B : public A {
public:
virtual bool Func()
{
cout << "I am B::Func\n";
return true;
}
};
class C : public A {
public:
virtual bool Func()
{
cout << "I am C::Func\n";
return true;
}
};
void main(void)
{
list<A *> x;
x.push_back(new B());
x.push_back(new C());
x.push_back(new B());
Örnek 2:
#include <list>
#include <iostream>
#include <algorithm>
#include <functional>
#include <string>
void main(void)
{
list<string> x;
x.push_back("Ali");
x.push_back("Mehmet");
x.push_back("Ziya");
x.push_back("Cenk");
x.push_back("Ali");
Cevap:
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
#include <stdlib.h>
#include <functional>
l.push_back("adana");
l.push_back("izmit");
l.push_back("sakarya");
l.push_back("samsun");
l.push_back("bolu");
l.push_back("sivas");
l.push_back("ankara");
iter = l.begin();
iter++;
(*iter).erase();
for (int i = 0; i < 3; i++)
iter++;
(*iter).erase();
system("pause");
}
pair Sınıfı
p = new X();
X s[sizeof(X) * SIZE];
new(s) X[SIZE];
delete p;
p->~X();
operator delete(p);
allocator Sınıfı
allocate() Fonksiyonu
#include <iostream>
#include <algorithm>
int main()
{
allocator<int> x;
allocator<int>: ointer p;
p = x.allocate(10, NULL);
memset(p, 0, 10 * sizeof(int));
return 0;
}
deallocate() Fonksiyonu
construct() Fonksiyonu
new(p) T(val);
class X {
public:
X()
{
cout << "default constructor called\n";
}
X(const X &r)
{
cout << "copy consturctor called\n";
m_a = r.m_a;
m_b = r.m_b;
}
~X()
{
cout << "destructor called\n";
}
int m_a, m_b;
};
int main()
{
allocator<X> x;
allocator<X>: ointer p;
p = x.allocate(1, NULL);
x.construct(p, X(10, 20));
return 0;
}
destroy() Fonksiyonu
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int myallocator<int>::m_val = 0;
int main()
{
vector<int, myallocator<int> > x;
x.push_back(10);
x.push_back(20);
int main()
{
listptr x;
x.push_back(new A());
x.push_back(new A());
}
Örneğin:
a = static_cast<int>(b);
Aslında normal tür dönüştürme operatörü bunların hepsinin
yerini tutar. Yeni operatörler belirli konularda uzmanlaştığı için
daha güvenli kabul edilmektedir.
static_cast Operatörü
const_cast Operatörü
p = const_cast<int *>(&x);
reinterpret_cast Operatörü
class type_info {
public:
virtual ~type_info();
bool operator==(const type_info &rhs) const;
bool operator!=(const type_info &rhs) const;
bool before(const type_info &rhs) const;
const char *name() const;
};
typeid Operatörü
typeid(ifade)
class A {
public:
virtual ~A() {}
};
class B : public A{
public:
virtual ~B() {}
};
void main()
{
A *pA = new B();
zeet06
Açık Profil bilgileri
28.11.08, 15:09 #5
zeet06
Vefakar Üye A
E
Şimdi D türünden bir nesne olsun bunun adresini doğrudan dönüştürme
yapmadan A türünden bir göstericiye atayabiliriz:
D d;
A *pA;
pA = &d;
#include <iostream>
struct A {
public:
virtual ~A() {}
};
struct B : A {
};
struct C : B {
};
struct D : C {
};
struct E : A {
};
void main()
{
D d;
A *pA;
pA = &d;
C *pC;
pC = dynamic_cast<C *>(pA);
if (pC == NULL)
cout << "gecersiz donusturme\n";
else
cout << "gecerli donusturme\n";
}
// 2. yontem
D &rD = static_cast<D &>(ra);
}
İkili ağaca yeni bir eleman eleneceği zaman önce eklenecek yer tepe
düğümünden hareketle sola ve sağa gidilerek bulunur. Sonra sol ya da
sağ düğüm üzerinde güncelleme yapılarak eleman eklenir. Örneğin
aşağıdaki sayıların teker teker buraya ekleneceğini düşünelim:
8 21 7 16 44 3 17 9 28 33
21
16
44
17
28
33
İkili ağaçta en tepeden en uzun dala kadar olan eleman sayısına ağacın
yüksekliği denir. Farklı dallarda ağacın yüksekliği aynı değilse bu tür
ikili ağaçlara dengelenmemiş ikili ağaçlar denir. Son kademe hariç tüm
dalların yüksekliği aynıysa böyle ağaçlara dengelenmiş ikili ağaç
(balanced binary tree) denir. Eğer son kademede de hiç fazla eleman
yoksa ağaç tam dengelenmiştir (complete binary tree). Dengelenmiş bir
ağaçta tamamen ikili arama performansına sahiptir, yani en kötü arama
sayısı 'dir. Yine dengelenmiş ikili ağaca en kötü olasılıkla logaritmik
olarak eleman eklenebilir. Bir dengelenmiş ikili ağaç basit bir kendi
kendini çağıran fonksiyonla sıralı olarak dolaşılabilir (binary tree
traversing), böylelikle ikili ağaçlar sıralı bir dizi görüntüsü de verebilir.
class multimap {
//...
};
map<int, string> x;
Örnek:
/* map.cpp */
#pragma warning(disable:4786)
#include <iostream>
#include <map>
#include <string>
int main()
{
map<int, string> x;
map<int, string>::iterator iter;
x.insert(make_pair(1, string("volkan")));
x.insert(make_pair(5, string("kaan")));
x.insert(make_pair(3, string("fatih")));
x.insert(make_pair(10, string("karga")));
x.insert(make_pair(7, string("murat")));
x.insert(make_pair(8, string("arda")));
return 0;
}
x[100] = "ali";
x[300] = "veli";
x[50] = "sacit";
//...
/* map2.cpp */
#pragma warning(disable:4786)
#include <iostream>
#include <map>
#include <string>
int main()
{
map<int, string> x;
map<int, string>::iterator iter;
x[100] = "volkan";
x[50] = "kaan";
x[57] = "falan";
x[54] = "filen";
return 0;
}
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <map>
int main() {
map<int, string> x;
map<int, string>::iterator iter;
x.insert(make_pair(10, string("ali")));
x.insert(make_pair(20, string("volkan")));
x.insert(make_pair(30, string("baris")));
x.insert(make_pair(40, string("emine")));
iter = x.find(30);
if (iter == x.end())
cout << "bulunamadı\n";
else
cout << (*iter).first << '\t' << (*iter).second << endl;
return 0;
}
3 8 8 8 9 10
2 4 6 9 10
lower_bound(5)
3 8 8 8 9 10
lower_bound(5)
/* map4.cpp */
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <map>
#include <vector>
int main()
{
vector<pair<int, string> > v;
multimap<int, string> x;
multimap<int, string>::iterator iter;
x.insert(make_pair(100, string("ali")));
x.insert(make_pair(10, string("volkan")));
x.insert(make_pair(10, string("baris")));
x.insert(make_pair(8, string("emine")));
iter = x.lower_bound(10);
return 0;
}
erase(elem);
erase(first, last);
Örneğin:
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <map>
int main()
{
multimap<int, string> x;
multimap<int, string>::iterator iter;
x.insert(make_pair(100, string("ali")));
x.insert(make_pair(10, string("volkan")));
x.insert(make_pair(10, string("baris")));
x.insert(make_pair(8, string("emine")));
x.erase(x.lower_bound(10), x.upper_bound(10));
return 0;
}
#pragma warning(disable:4786)
#include <iostream>
#include <cstdlib>
#include <set>
int main()
{
set<int> x;
return 0;
}
/* set2.cpp */
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <set>
class Person {
public:
Person(const char *pName, int no):m_name(pName),
m_no(no){}
friend ostream &operator<<(ostream &r, const Person
&per);
bool operator<(const Person &per) const
{
return m_no < per.m_no;
}
private:
string m_name;
int m_no;
};
int main()
{
set<Person> x;
x.insert(Person("ali", 10));
x.insert(Person("volkan", 20));
x.insert(Person("veli", 30));
return 0;
}
Smart gösterici sistemlerinde bir asıl sınıf vardır, bir de -> operatör
fonksiyonu yazılmış asıl sınıf türünden gösterici gibi davranan sınıf
vardır. Hatta bazen asıl sınıf tamamen gizlenebilir, asıl sınıfa tamamen
smart göstericiler ile erişilebilir.
class X {
public:
X(int a):m_a(a) {}
void Disp() { cout << m_a << endl; }
Private:
int m_a;
};
auto_ptr Sınıfı
X::X()
{
m_p = new A();
-> throw işlemi yapıldı
}
Burada m_a’nın içerisinde throw oluşmuşsa hiç bir sınıf için bitiş
fonksiyonu çağırılmaz. İşte auto_ptr bu tür durumlarda otomatik
bitiş fonksiyonu çağırılsın diye kullanılan smart gösterici sınıfıdır.
#include <memory>
#include <iostream>
class A {
public:
A(int a) : m_a(a){}
~A(){}
void Disp() { std::cout << m_a << std::endl; }
private:
int m_a;
};
void Func(auto_ptr<A> x)
{
x->Disp();
}
void main(void)
{
auto_ptr<A> a(new A(10));
auto_ptr<A> b;
a->Disp();
b = a;
b->Disp();
Func(b);
}
class B {
public:
B(int b)
{
m_b = b;
throw m_b;
}
private:
int m_b;
};
class A {
public:
A(int a, int b):m_pi(new int(a)), m_pB(new B(b)) {}
private:
auto_ptr<int> m_pi;
auto_ptr<B> m_pB;
};
int main()
{
try {
A a(10, 20);
}
catch(...) {
cout << "exception...\n";
}
return 0;
}
#include <iostream>
#include <memory>
class B {
public:
B(int b)
{
m_b = b;
}
void Disp()
{
cout << m_b << endl;
}
private:
int m_b;
};
class A {
public:
A(int a, int b):m_pi(new int(a)), m_pB(new B(b)) {}
void Func() throw()
{
cout << *m_pi << endl;
m_pB->Disp();
}
private:
auto_ptr<int> m_pi;
auto_ptr<B> m_pB;
};
int main()
{
try {
A a(10, 20);
a.Func();
}
catch(...) {
cout << "exception...\n";
}
return 0;
}
Çoklu Türetme
Bir sınıfın birden fazla taban sınıfı olması durumuna çoklu türetme
(multiple inheritance) denir. Çoklu türetme türetilmiş nesnelerin içsel
organizasyonu standart olarak belirlenmemiştir. Ancak derleyicilerin
çoğu sol kolun tepesinden aşağıda soldan sağa elemanları ardışık
dizerler. Örneğin:
A
B
C c; ->
class A {
public:
void Func()
{
cout << "I am A::Func\n";
}
};
class B {
public:
void Func()
{
cout << "I am B::Func";
}
};
int main()
{
C c;
return 0;
}
Çoklu türetilmiş bir sınıf nesnesinin adresini her taban sınıfına ilişkin
bir göstericiye doğrudan atayabiliriz. Şüphesiz bu durumda çoklu
türetilmiş sınıfın ilgili taban sınıf verilerinin bulunduğu bloğun adresi
elde edilecektir. Örneğin:
C c;
B *pB;
A *pA;
pB = &c;
pA = &c;
...
A
pA
pB
class B {
public:
virtual void FuncB() {}
};
//...
};
class B : public A {
//...
C
};
class C : public A {
//...
D
};
istream
ostream
iostream
class A {
//...
};
deque Sınıfı
.....
.....
.....
queue Sınıfı
queue tipik olarak FIFO kuyruk sistemidir. Bilindiği gibi gibi LIFO
sistemlerine stack denir. queue sınıfı bir adaptör sınıftır ve
<queue> başlık dosyasında bulunur. Bildirimi şöyledir.
return 0;
}
C++, C’nin bir üst kümesidir. Pek çok durumda *.c uzantılı bir dosya
C++ derleyicisi tarafından başarılı olarak derlenir. Ancak bazı küçük
uyumsuzluklar da bulunmaktadır.
struct X a;
int X;