C++ I/O sistemi akışlar aracılığıyla çalışır. Akış, bilgi üreten veya kullanan mantıksal bir aygıt olarak tanımlanabilir. Bir akış, I/O sistemi tarafından fiziksel bir cihaza bağlanır. Bağlı oldukları gerçek fiziksel cihazlar farklı olsa bile tüm akışların çalışma şekli aynıdır. Tüm akışlar aynı şekilde çalıştığından, aynı I/O fonksiyonları hemen hemen her tür fiziksel aygıtta çalışabilir. Örneğin, bir dosyaya veri yazmak için kullandığımız bir fonksiyonu bir yazıcıya veya ekrana yazmak için de kullanabiliriz.
C++ I/O sistemi fonksiyonları ile sınıf bildirimleri <iostream> başlık dosyasında yer alır.
I/O sınıf bildirimleri şablon sınıflarla başlar. Bir şablon sınıf tanımlandıktan sonra, biri 8 bitlik karakterler ve diğeri geniş karakterler olmak üzere iki farklı şablon fonksiyonu için yeniden bildirim yapılır.
C++ I/O sistemi, birbiriyle ilişkili ancak farklı iki şablon sınıfı hiyerarşisi üzerine kurulmuştur.
Birincisi, basic_streambuf adı verilen düşük seviyeli I/O sınıfından türetilmiştir. Bu sınıf, temel, düşük seviyeli giriş ve çıkış işlemlerini ve tüm C++ I/O sistemi için temel desteği sağlar. Gelişmiş I/O programlama yapmadığımız sürece, doğrudan basic_streambuf kullanmamıza gerek kalmayacaktır.
En sık çalışılan sınıflar ise, basic_ios sınıfından türetilmiştir. Bu, akış I/O ile ilgili biçimlendirme, hata kontrolü ve durum bilgileri sağlayan üst düzey bir I/O sınıfıdır. Basic_ios sınıfı, basic_istream, basic_ostream ve basic_iostream gibi türetilmiş sınıfların türetilmesinde ana sınıf olarak kullanılır. Bu sınıflar sırasıyla giriş, çıkış ve giriş/çıkış yapabilen akışlar oluşturmak için kullanılır.
Aağıdaki listede 8 bit ve geniş karakterler için kullanılan şablon sınıf isimleri yer almaktadır:
Şablon sınıfı | 8 bit karakter şablon sınıfı | Geniş karakter şablon sınıfı |
---|---|---|
basic_streambuf | streambuf | wstreambuf |
basic_ios | ios | wios |
basic_istream | istream | wistream |
basic_ostream | ostream | wostream |
basic_iostream | iostream | wiostream |
Bir C++ programı çalışmaya başladığında, dört adet akış otomatik olarak açılır.
Akış | Görevi | Aygıt | |
---|---|---|---|
8 bit karakter | Geniş karakter | ||
cin | win | Standart veri girişi | Klavye |
cout | wout | Standart çıkış | Ekran |
cerr | werr | Standart hata çıkışı | Ekran |
clog | wlog | cerr tamponlanmış sürümü | Ekran |
Normal olarak, standart akışlar klavye ve ekran ile işlem yapar. Ancak, ihtiyaç duyulduğunda, dosya veya diğer aygıtlara yönlendirilebilir.
C++'da, giriş ve çıkış işlemlerinde verileri, alan genişliklerini ayarlayarak, sayıların hangi sayı sistemine göre gösterileceğini belirleyerek veya ondalık sayılarda noktadan sonra kaç rakam görüntüleneceğini belirleyerek, yapılandırabiliriz.
C++'da, verileri yapılandırmak için iki farklı yöntem izleyebiliriz.
1. ios sınıfının üyelerine doğrudan erişerek, sınıf içindeki üye fonksiyonları ve değişkenleri kullanabiliriz.
2. Manipülatörler adı verilen özel fonksiyonları kullanabiliriz.
Her akış, verinin yapılandırma şeklini kontrol eden bir dizi format işaretiyle ilişkilendirilmiştir. ios sınıfı içinde, aşağıdaki değerlerin tanımlandığı fmtflags adlı bir numaralandırma yer alır.
CodeBlocks 17.12 sürümünde ios_base.h başlık dosyasının içinde _Ios_Fmtflags isimli bir numaralandırma yapılmış ve typedef anahtar kelimesi ile fmtflags adlı bir veri türü tanımlanmıştır. Sonra, _Ios_Fmtflags içinde yer alan her bir değer için fmtflags veri türünden (numaralandırma) statik sabit bir değer oluşturulmuştur.
enum _Ios_Fmtflags
{
_S_boolalpha = 1L << 0,
_S_dec = 1L << 1,
_S_fixed = 1L << 2,
_S_hex = 1L << 3,
_S_internal = 1L << 4,
_S_left = 1L << 5,
_S_oct = 1L << 6,
_S_right = 1L << 7,
_S_scientific = 1L << 8,
_S_showbase = 1L << 9,
_S_showpoint = 1L << 10,
_S_showpos = 1L << 11,
_S_skipws = 1L << 12,
_S_unitbuf = 1L << 13,
_S_uppercase = 1L << 14,
_S_adjustfield = _S_left | _S_right | _S_internal,
_S_basefield = _S_dec | _S_oct | _S_hex,
_S_floatfield = _S_scientific | _S_fixed,
_S_ios_fmtflags_end = 1L << 16
};
typedef _Ios_Fmtflags fmtflags;
static const fmtflags boolalpha = _S_boolalpha;
static const fmtflags dec = _S_dec;
static const fmtflags fixed = _S_fixed;
static const fmtflags hex = _S_hex;
static const fmtflags internal = _S_internal;
static const fmtflags left = _S_left;
static const fmtflags oct = _S_oct;
static const fmtflags right = _S_right;
static const fmtflags scientific = _S_scientific;
static const fmtflags showbase = _S_showbase;
static const fmtflags showpoint = _S_showpoint;
static const fmtflags showpos = _S_showpos;
static const fmtflags skipws = _S_skipws;
static const fmtflags unitbuf = _S_unitbuf;
static const fmtflags uppercase = _S_uppercase;
static const fmtflags adjustfield = _S_adjustfield;
static const fmtflags basefield = _S_basefield;
static const fmtflags floatfield = _S_floatfield;
Başlık dosyasında (ios_base.h) yer alan sabit değerlerin kullanım amaçları aşağıdaki tabloda yer almaktadır:
Şablon sınıfı | Kullanım amacı |
---|---|
adjustfield | Hizalama özelliğini ayarlayan left, right ve internal değerlerinin her üçünü birden ayarlar. |
basefield | Sayı sistemlerini temsil eden dec, oct ve hex değerlerinin her üçünü birden ayarlar. |
boolalpha | Ayarlandığında, ikili sayı sistemi değerleri true ve false anahtar kelimleri ile giriş yapılabilir veya çıktı alınabilir. |
dec | Sayısal değer çıkışını ondalık sayı sistemine ayarlar. |
fixed | Ayarlandığında, float değerler normal yöntemle görüntülenir. |
floatfield | Float değerlerin görüntülenmesini belirleyen scientific ve fixed değerlerinin her ikisini birden ayarlar. |
hex | Sayısal değer çıkışını onaltılık sayı sistemine ayarlar. |
internal | Ayarlandığında, herhangi bir işaret veya temel karakter arasına boşluklar ekleyerek bir alanı doldurmak için sayısal bir değer doldurulur. |
left | Ayarlandığında, çıktı sola hizalanır. |
oct | Sayısal değer çıkışını sekizlik sayı sistemine ayarlar. |
right | Ayarlandığında, çıktı sağa hizalanır (Ön tanımlı). |
scientific | Ayarlandığında, float değerler bilimsel gösterim ile görüntülenir. |
showbase | Ayarlandığında, sayısal değerlerin sayı sisteminin gösterilmesini sağlar. |
showpoint | Ayarlandığında, gerekli olsun veya olmasın tüm float değerler için ondalık noktası ve sondaki sıfırların görüntülenmesini sağlar. |
showpos | Ayarlandığında, pozitif değerlerden önce bir artı işareti görüntülenir. |
skipws | Ayarlandığında, bir akışa giriş yapılırken başta yer alan boşluk, sekme ve yeni satır gibi beyaz boşluk karakterleri atılır. Devre dışı bırakıldığında, beyaz boşluk karakterleri kabul edilir. |
unitbuf | Ayarlandığında, tampon her ekleme işleminden sonra boşaltılır. |
uppercase | Ayarlandığında, bilimsel gösterim için kullanılan e ve onaltılık değerlerin gösterimi için kullanılan karakterler büyük harf olarak görüntülenir. |
Bu listede yer alan değerler veri yapılandırma flag değerlerini ayarlamak için kullanılır.
Şimdi, veri yapılandırma flag değerlerinin ön tanımlı değerlerini yazdıracağımız bir örneği incelemeye çalışalım:
#include <iostream>
#include <stdlib.h>
using namespace std;
void deger_yaz(const char *cdeger, long int val)
{
int bitsayi = 32;
char *cdizi = (char*) malloc(bitsayi+1);
cdizi[bitsayi] = '\0';
unsigned long int u = *(unsigned long int*)&val;
unsigned long int mask = 1 << (bitsayi-1);
int id;
for (id=0; id<bitsayi; id++, mask >>= 1) {
cdizi[id] = (u & mask) ? '1' : '0';
}
printf("%-15s %5ld : %s\n", cdeger, val, cdizi);
free(cdizi);
}
int main(void)
{
deger_yaz((const char*) "boolalpha", ios::boolalpha);
deger_yaz((const char*) "dec", ios::dec);
deger_yaz((const char*) "fixed", ios::fixed);
deger_yaz((const char*) "hex", ios::hex);
deger_yaz((const char*) "internal", ios::internal);
deger_yaz((const char*) "left", ios::left);
deger_yaz((const char*) "oct", ios::oct);
deger_yaz((const char*) "right", ios::right);
deger_yaz((const char*) "scientific", ios::scientific);
deger_yaz((const char*) "showbase", ios::showbase);
deger_yaz((const char*) "showpoint", ios::showpoint);
deger_yaz((const char*) "showpos", ios::showpos);
deger_yaz((const char*) "skipws", ios::skipws);
deger_yaz((const char*) "unitbuf", ios::unitbuf);
deger_yaz((const char*) "uppercase", ios::uppercase);
deger_yaz((const char*) "adjustfield", ios::adjustfield);
deger_yaz((const char*) "basefield", ios::basefield);
deger_yaz((const char*) "floatfield", ios::floatfield);
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
boolalpha 1 : 00000000000000000000000000000001 dec 2 : 00000000000000000000000000000010 fixed 4 : 00000000000000000000000000000100 hex 8 : 00000000000000000000000000001000 internal 16 : 00000000000000000000000000010000 left 32 : 00000000000000000000000000100000 oct 64 : 00000000000000000000000001000000 right 128 : 00000000000000000000000010000000 scientific 256 : 00000000000000000000000100000000 showbase 512 : 00000000000000000000001000000000 showpoint 1024 : 00000000000000000000010000000000 showpos 2048 : 00000000000000000000100000000000 skipws 4096 : 00000000000000000001000000000000 unitbuf 8192 : 00000000000000000010000000000000 uppercase 16384 : 00000000000000000100000000000000 adjustfield 176 : 00000000000000000000000010110000 basefield 74 : 00000000000000000000000001001010 floatfield 260 : 00000000000000000000000100000100
Program, deger_yaz() fonksiyonunu kullanarak ios_base.h dosyasındaki veri yapılandırma değerlerinin önce isimlerini sonra onluk ve ikili sayı sistemine göre değerlerini ekrana yazar.
Veri yapılandırma değerlerine doğrudan erişim sağlandığından, değer isimlerinden önce ios:: ön eki kullanılmaktadır.
Fmtflags içinde yer alan flag değerlerini ayarlamak ve devre dışı bırakmak için ios sınıfının elemanları olan setf() ve unsetf() fonksiyonları kullanılır. Fonksiyonların genel yapısı aşağıda gösterilmektedir.
fmtflags setf(fmtflags flags);
void unsetf(fmtflags flags);
Setf() fonksiyonu önceki veri yapılandırma flag değerlerini geri döndürür ve flags ile tanımlanan değerleri devreye sokar. Unsetf() fonksiyonu flags ile tanımlanan değerleri devreden çıkarır ve herhangi bir değer geri döndürmez.
Sayısal değer çıkışını onaltılık sayı sistemine ayarlamak için aşağıdaki işlem satırı kullanılır.
akış-türü.setf(ios::hex);
Burada, akış-türü veri yapılandırmasını ayarlamak istediğimiz akışı göstermektedir.
Şimdi, setf() ve unsetf() fonksiyonlarının çalışmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
int main(void)
{
cout << 795248 << endl;
cout.unsetf(ios::dec);
cout.setf(ios::hex);
cout.setf(ios::showbase);
cout << 795248 << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
795248 0xc2270
Program, önce long int bir değeri onlu sayı sisteminde ekrana yazar. Sonra, unsetf() fonksiyonunu kullanarak onluk sayı sisteminin devreden çıkarır, setf() fonksiyonunu kullanarak onaltılık sayı sistemini ve sayı sistemini gösterme özelliğini devreye sokar. Son olarak, long int değeri onaltılık sayı sisteminde ekrana yazar.
Yukarıdaki programın yaptığı işlemin aynısını sadece setf() fonksiyonu kullanarak yapabiliriz. Çoklu görev tanımlanarak iki parametre alacak şekilde düzenlenmiş setf() fonksiyonunun genel yapısı aşağıda gösterilmektedir:
fmtflags setf(fmtflags flag1, fmtflags flag2);
Bu durumda, sadece flag2 ile gösterilen flag değerleri etkilenir. Bu parametrede gösterilen flag değerleri tamamen devre dışı bırakılır, sonra flag1 parametresi ile gösterilen flag değerleri devreye sokulur. Flag2 parametresinde yer almayan flag değerlerine flag1 parametresinde tanımlansa bile herhangi bir işlem yapılmaz. Önceki veri yapılandırma flag değerleri geri döndürülür.
Şimdi, bu özelliği bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
int main(void)
{
cout << 795248 << endl;
cout.setf(ios::hex, ios::basefield);
cout.setf(ios::showbase);
cout << 795248 << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
795248 0xc2270
Program, önce long int bir değeri onlu sayı sisteminde ekrana yazar. Sonra, sadece setf() fonksiyonunu kullanarak ikinci parametre ile tüm sayı sistemlerini devreden çıkarır, birinci parametre ile onaltılık sayı sistemini devreye sokar. Yine setf() fonksiyonunu kullanarak sayı sistemini gösterme özelliğini devreye sokar. Son olarak, long int değeri onaltılık sayı sisteminde ekrana yazar.
Veri yapılandırma flag değerlerini okumak için ios sınıfı içinde yer alan flags() üye fonksiyonunu kullanabiliriz. Her bir flag değerinin aktif ayarını geri döndüren flags() fonksiyonunun genel yapısı aşağıdaki şekildedir:
fmtflags flags();
Şimdi, flags() fonksiyonunun çalışmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
void deger_yaz(void)
{
ios::fmtflags fmt = cout.flags(); // flag değerlerini okuma
int bitsayi = 32;
unsigned long int mask = 1 << (bitsayi-1); // 2.147.483.648
cout << fmt << " = ";
for ( ; mask; mask >>= 1) {
cout << ((fmt & mask) ? "1" : "0");
}
cout << endl;
}
int main(void)
{
deger_yaz(); // Mevcut flag değerini ekrana yazar.
cout.setf(ios::basefield); // dec, hex ve oct flag değerlerini aktif hale getirir.
deger_yaz(); // Değişiklik sonrası flag değerini ekrana yazar.
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
4098 = 00000000000000000001000000000010 4170 = 00000000000000000001000001001010
Program, önce deger_yaz() fonksiyonu içinde flags() fonksiyonunu kullanarak mevcut veri yapılandırma flag değerlerini ekrana yazar. Sonra, setf() fonksiyonu ile basefield flag değerini devreye sokarak dec, hex ve oct flag devrelerini aktif hale getirir. Daha sonra, deger_yaz() fonksiyonu içinde flags() fonksiyonunu kullanarak veri yapılandırma flag değerlerinin son halini tekrar ekrana yazar.
Bir akış ile ilgili veri yapılandırma flag değerlerini değiştirmek için ios sınıfı içinde yer alan flags() üye fonksiyonunu parametre atayarak kullanabiliriz. Önceki ayarları geri döndüren flags() fonksiyonunun genel yapısı aşağıdaki şekildedir:
fmtflags flags(fmtflags flags);
Şimdi, flags() fonksiyonunun bir parametre değeri alarak çalışmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
void deger_yaz(void)
{
ios::fmtflags fmt = cout.flags(); // flag değerlerini okuma
int bitsayi = 32;
unsigned long int mask = 1 << (bitsayi-1); // 2.147.483.648
cout << fmt << " = ";
for ( ; mask; mask >>= 1) {
cout << ((fmt & mask) ? "1" : "0");
}
cout << endl;
}
int main(void)
{
deger_yaz(); // Mevcut flag değerini ekrana yazar.
// dec, hex ve oct flag değerlerini aktif hale getirir. Diğer tüm flag değerlerini devre dışı bırakır.
cout.flags(ios::basefield);
deger_yaz(); // Değişiklik sonrası flag değerini ekrana yazar.
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
4098 = 00000000000000000001000000000010 74 = 00000000000000000000000001001010
Program, önce deger_yaz() fonksiyonu içinde flags() fonksiyonunu kullanarak mevcut veri yapılandırma flag değerlerini ekrana yazar. Sonra, flags() fonksiyonunu ios::basefield parametresi ile kullanarak, dec, hex ve oct flag değerlerini aktif hale getirir. Diğer tüm flag değerleri devreden çıkarılır. Daha sonra, deger_yaz() fonksiyonu içinde flags() fonksiyonunu kullanarak veri yapılandırma flag değerlerinin son halini tekrar ekrana yazar.
Bir önceki programda, setf() fonksiyonu ile kullanılan ios::basefield parametresi sadece dec, hex ve oct değerlerini aktif hale getirirken, burada flags() fonksiyonu ile kullanıldığında, dec, hex ve oct değerlerini aktif hale getirirken diğer tüm değerleri devreden çıkarır.
Bir akış ile ilgili verinin çıkışını almak istediğimizde, veri içinde yer alan karakter sayısı kadar yer kaplar. İhtiyaç duyulduğunda, ios sınıfı içinde yer alan width() üye fonksiyonunu kullanabiliriz. Fonksiyonun genel yapısı aşağıdaki şekildedir:
streamsize width(streamsize width);
streamsize width();
Burada width alan genişliğini gösterir. Fonksiyon önceki alan genişliğini geri döndürür. Streamsize veri türü int veri türünden tanımlanmış bir değerdir.
Fonksiyon parametresiz olarak kullanıldığında, aktif alan genişliğini verir.
Minimum alan genişliği ayarlandıktan sonra, bir değer belirtilen genişlikten daha az karakter içeriyorsa, boş kalan alan aktif doldurma karakteriyle (varsayılan değer boşluk karakteridir) doldurulur. Değer minimum alan genişliğinden daha fazla karakter içeriyorsa, alan otomatik olarak genişletilecek ve herhangi bir değer iptal edilmeyecektir.
Şimdi, width() fonksiyonunun kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
int main(void)
{
cout << 21.754f << endl;
cout.width(10);
cout << 21.754f << endl;
cout << 526 << endl;
cout.width(10);
cout << 526 << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
21.754 21.754 526 526
Program, önce float bir değeri ekrana yazar. Veri genişliğini width() fonksiyonu ile 10 değerine ayarladıktan sonra aynı bir değeri tekrar ekrana yazar. Toplam genişlik 10 ve veri genişliği 6 karakter olduğundan verinin sol tarafına 4 adet boşluk karakteri eklenir. Aynı işlemler int bir değer için tekrarlanır.
Bir akış ile ilgili ondalıklı sayıların çıkışını almak istediğimizde, ondalık bölüm karakter sayısını belirlemek için, ios sınıfı içinde yer alan precision() üye fonksiyonunu kullanabiliriz. Fonksiyonun genel yapısı aşağıdaki şekildedir:
streamsize precision(streamsize pre);
streamsize precision();
Burada pre ondalık sayı karakter sayısını gösterir. Fonksiyon önceki karakter sayısını geri döndürür. Ön tanımlı değer 6'dır.
Aslında pre ondalıklı bir sayıda yer alan tüm rakamların sayısını belirlemektedir. Nokta işaretinin sol tarafında kalan tamsayı rakam sayısını pre değerinden çıkardığımız için, nokta işaretinin sağ tarafında kalan ondalıklı bölüm rakam sayısı dolaylı olarak belirlenmektedir.
Fonksiyon parametresiz olarak kullanıldığında, aktif ondalık sayı karakter sayısını verir.
Şimdi, precision() fonksiyonunun kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
int main(void)
{
cout << 125.83541f << endl;
cout.precision(8);
cout << 125.83541f << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
125.835 125.83541
Program, önce 3 basamaklı tamsayı ve 5 basamaklı olmak üzere toplam 8 basamaklı float bir değeri ekrana yazar. Ancak, ön tanımlı basamak sayısı 6 olduğundan, ondalık kısmının son iki rakamı yazılmaz. Ondalık sayı basamak sayısını, precision() fonksiyonu ile 8 değerine ayarladıktan sonra, aynı değeri tekrar ekrana yazar. Bu kez sayı normal bir şekilde ekrana yazılır.
Bir akış ile ilgili verinin çıkışını almak istediğimizde, verilen değer ön tanımlı veya width() fonksiyonu tarafından belirtilen genişlikten daha az karakter içeriyorsa, boş kalan alan, varsayılan değer olan boşluk karakteri ile doldurulur. Ön tanımlı boşluk karakterini değiştirmek için, ios sınıfı içinde yer alan fill() üye fonksiyonunu kullanabiliriz. Fonksiyonun genel yapısı aşağıdaki şekildedir:
char fill(char cd);
char fill();
Burada, cd yeni boşluk doldurma karakteri olarak belirlenir. Fonksiyon önceki boşluk doldurma karakterini geri döndürür.
Fonksiyon parametresiz olarak kullanıldığında, aktif boşluk doldurma karakterini verir.
Şimdi, fill() fonksiyonunun kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
int main(void)
{
cout.width(10);
cout << 7452 << endl;
cout.width(10);
cout.fill('&');
cout << 7452 << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
7452 &&&&&&7452
Program, veri genişliğini 10 karakter olarak ayarlayarak 4 basamaklı bir int değeri ekrana yazar. Ön tanımlı boşluk doldurma karakteri boşluk karakteri olduğundan sayının sol tarafına 4 adet boşluk karakteri ekler. Veri genişliğini tekrar 10 karakter olarak ayarladıktan ve fill() fonksiyonu ile boşluk doldurma karakterini & olarak ayarladıktan sonra, int değeri tekrar ekrana yazar. Bu kez sayının sol tarafına 4 adet & karakteri ekler.
Bir akışın veri yapılandırma parametrelerini değiştirmek için manipülatörler adı verilen özel fonksiyonlar da kullanabiliriz. Sistemde tanımlı olan manipülatörler aşağıdaki tabloda yer almaktadır:
Manipülatör | Kullanım amacı |
---|---|
boolalpha | Boolalpha flag değerini devreye sokar. |
dec | Dec flag değerini devreye sokar. |
endl | Çıkışa bir yeni satır karakteri gönderir ve akışı temizler. |
ends | Çıkışa boş bir değer gönderir. |
fixed | Fixed flag değerini devreye sokar. |
flush | Bir akışı temizler. |
hex | Hex flag değerini devreye sokar. |
internal | Internal flag değerini devreye sokar. |
left | Left flag değerini devreye sokar. |
nobooalpha | Boolalpha flag değerini devreden çıkarır. |
noshowbase | Showbase flag değerini devreden çıkarır. |
noshowpoint | Showpoint flag değerini devreden çıkarır. |
noshowpos | Showpos flag değerini devreden çıkarır. |
noskipws | Skipws flag değerini devreden çıkarır. |
nounitbuf | Unitbuf flag değerini devreden çıkarır. |
nouppercase | Uppercase flag değerini devreden çıkarır. |
oct | Oct flag değerini devreye sokar. |
resetiosflags | Kendisine parametre olarak geçirilen fmtflags içinde yer alan flag değerlerini devreden çıkarır. |
right | Right flag değerini devreden çıkarır. |
scientific | Scientific flag değerini devreye sokar. |
setbase | Sayı sistemini kendisine parametre olarak geçirilen değere ayarlar. |
setfill | Doldurma karakterini kendisine parametre olarak değere ayarlar. |
setiosflags | Kendisine parametre olarak geçirilen fmtflags içinde yer alan flag değerlerini devreye sokar. |
setprecision | Ondalık sayı rakam sayısını kendisine parametre olarak değere ayarlar. |
setw | Veri genişliğini kendisine parametre olarak değere ayarlar. |
showbase | Showbase flag değerini devreye sokar. |
showpoint | Showpoint flag değerini devreye sokar. |
showpos | Showpos flag değerini devreye sokar. |
skipws | Skipws flag değerini devreye sokar. |
unitbuf | Unitbuf flag değerini devreye sokar. |
uppercase | Uppercase flag değerini devreye sokar. |
ws | Verinin önünde yer alan beyaz boşluk karakterlerini dikkate almaz. |
Parametre alan manipülatörleri kullanmak için, <iomanip> başlık dosyasını programlarımızın başına eklememiz gerekir.
Veri yapılandırmasında ios sınıfı içinde yer alan fonksiyonlar ayrı bir işlem satırında kullanılırken, manipülatör fonksiyonları akışların yer aldığı satırlarda kullanılabilmektedir.
Şimdi, akış veri genişliğini ayarlama işleminin iki farklı yöntemle yapılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
#include <iomanip>
using namespace std;
int main(void)
{
// ios sınıfı width() fonksiyonu ile akış veri genişliğini ayarlama
cout.width(10);
cout << 142.843f << endl;
// Manipülatör ile akış veri genişliğini ayarlama
cout << setw(10) << 142.843f << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
142.843 142.843
Program, akış veri genişliğini önce ios sınıfı width() fonksiyonu ile yaparak float bir değeri ekrana yazar. Sonra, aynı işlemi manipülatör setw() ile yaparak değeri tekrar ekrana yazar.
Şimdi, akış verisini onaltılık sayı sistemine geçirme işleminin iki farklı yöntemle yapılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
#include <iomanip>
using namespace std;
int main(void)
{
cout << 46325 << endl;
// ios sınıfı setf() fonksiyonu ile onaltılık sayı sistemine geçiş
cout.setf(ios::hex, ios::basefield);
cout.setf(ios::showbase);
cout << 46325 << endl;
// Manipülatör ile onaltılık sayı sistemine geçiş
cout << hex << showbase << 46325 << endl;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
46325 0xb4f5 0xb4f5
Program, akış veri genişliğini önce ios sınıfı setf() fonksiyonu ile yaparak int bir değeri ekrana yazar. Sonra, aynı işlemi hex() ve showbase() manipülatörleri ile yaparak değeri tekrar ekrana yazar.
C++'da, << ve >> işlemcilerini mevcut ve kullanıcı tanımlı veri tipleri üzerinde giriş ve çıkış işlemleri yapabilmek için çoklu görev tanımlama işlemi uygulayabiliriz.
Oluşturduğumuz bir sınıf için aşağıdaki genel yapı ile bir << işlemcisine çoklu görev tanımlama işlemi uygulayabiliriz:
ostream &operator<<(ostream &stream, const sınıf_adı &nesne)
{
// Kod bloğu
return stream;
}
İlk parametre çıkış akışını gösteren bir akış olmalıdır.
İkinci parametre çıkış işlem yapılacak sınıftan oluşturulan bir nesne referansı olmalıdır.
Çoklu görev tanımlama işlemi ile elde edilen << fonksiyonu mutlaka stream değeri geri döndürmelidir.
Şimdi, çoklu görev tanımlama işlemi yapılmış bir << işlemcisinin kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
class sinif {
public:
int pubid1, pubid2, pubid3;
sinif(int pid1, int pid2, int pid3)
{
pubid1 = pid1; pubid2 = pid2; pubid3 = pid3;
}
void deger_goster()
{
cout << "Değişken değerleri (Sınıf fonksiyonu ile): " << pubid1 << " " << pubid2 << " " << pubid3 << endl;
}
};
// Çoklu görev tanımlanmış << işlemci fonksiyonu
ostream &operator<<(ostream &stream, const sinif &nes)
{
stream << "Değişken değerleri (<< fonksiyonu ile): " << nes.pubid1 << " " << nes.pubid2 << " " << nes.pubid3 << endl;
return stream;
}
int main(void)
{
sinif nes(7, 21, 143);
nes.deger_goster(); // Değerlerin sınıf fonksiyonu ile gösterilmesi
cout << nes; // Değerlerin çoklu görev tanımlanmış << işlemci fonksiyonu ile gösterilmesi
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
Değişken değerleri (Sınıf fonksiyonu ile): 7 21 143 Değişken değerleri (<< fonksiyonu ile): 7 21 143
Program, üç adet public int değer, bu int değerlere birer değer atayan constructor fonksiyonu ve bu değerleri ekranda gösteren bir fonksiyon içeren bir sınıf oluşturur. Ayrıca, << işlemcisine çoklu görev tanımlayarak, kendisine geçirilen nesne yoluyla bir sınıf kopyası içindeki değişken değerlerini ekranda gösteren, bir fonksiyon oluşturur. Sonra, oluşturduğu sınıftan bir nesne bildirimi yapar. Nesne yoluyla, sınıf kopyası içinde yer alan değişken değerlerini önce sınıf fonksiyonu ile sonra da çoklu görev tanımlama ile oluşturulan fonksiyon ile ekrana yazar.
Programda, << işlemcisine çoklu görev tanımlama ile oluşturulan fonksiyon sınıfın bir elemanı olarak tanımlanmamıştır.
veri-türü sınıf-adı::operator<<(sınıf_adı nesne)
ostream &operator<<(ostream &stream, const sınıf_adı &nesne)
Bu nedenlerle, << işlemcisi ile yapılan çoklu görev tanımlama ile oluşturulan fonksiyonlar işlem yaptıkları sınıfın üyesi olamazlar.
<< işlemcisine çoklu görev tanımlama uygulanarak oluşturulan fonksiyonlar işlem yaptıkları sınıfın bir üyesi olamadığından, sınıfın private elemanlarına erişim sağlamaları için, bu fonksiyonlar friend fonksiyon olarak tanımlanır.
Şimdi, bu özelliği bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
class sinif {
private:
int priid1, priid2, priid3;
public:
sinif(int pid1, int pid2, int pid3)
{
priid1 = pid1; priid2 = pid2; priid3 = pid3;
}
void deger_goster()
{
cout << "Değişken değerleri (Sınıf fonksiyonu ile): " << priid1 << " " << priid2 << " " << priid3 << endl;
}
friend ostream &operator<<(ostream &stream, const sinif &nes);
};
// Çoklu görev tanımlanmış << işlemci fonksiyonu
ostream &operator<<(ostream &stream, const sinif &nes)
{
stream << "Değişken değerleri (<< fonksiyonu ile): " << nes.priid1 << " " << nes.priid2 << " " << nes.priid3 << endl;
return stream;
}
int main(void)
{
sinif nes(7, 21, 143);
nes.deger_goster(); // Değerlerin sınıf fonksiyonu ile gösterilmesi
cout << nes; // Değerlerin çoklu görev tanımlanmış << işlemci fonksiyonu ile gösterilmesi
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
Değişken değerleri (Sınıf fonksiyonu ile): 7 21 143 Değişken değerleri (<< fonksiyonu ile): 7 21 143
Program, üç adet private int değer, bu int değerlere birer değer atayan constructor fonksiyonu ve bu değerleri ekranda gösteren bir fonksiyon içeren bir sınıf oluşturur. Ayrıca, << işlemcisine çoklu görev tanımlayarak, kendisine geçirilen nesne yoluyla bir sınıf kopyası içindeki değişken değerlerini ekranda gösteren, friend bir fonksiyon oluşturur. Sonra, oluşturduğu sınıftan bir nesne bildirimi yapar. Nesne yoluyla, sınıf kopyası içinde yer alan değişken değerlerini önce sınıf fonksiyonu ile sonra da çoklu görev tanımlama ile oluşturulan fonksiyon ile ekrana yazar.
Oluşturduğumuz bir sınıf için aşağıda genel yapı ile bir >> işlemcisine çoklu görev tanımlayabiliriz:
istream &operator>>(istream &stream, sınıf_adı &nesne)
{
// Kod bloğu
return stream;
}
İlk parametre giriş akışını gösteren bir akış olmalıdır.
İkinci parametre giriş işlemi yapılacak sınıftan oluşturulan bir nesne referansı olmalıdır.
Çoklu görev tanımlama ile elde edilen >> fonksiyonu mutlaka stream değeri geri döndürmelidir.
Şimdi, bu özelliği bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
class sinif {
private:
int priid1, priid2, priid3;
public:
sinif() {
priid1=0; priid2=0; priid3=0;
}
sinif(int pid1, int pid2, int pid3)
{
priid1 = pid1; priid2 = pid2; priid3 = pid3;
}
void deger_goster(void)
{
cout << "Değişken değerleri (Sınıf fonksiyonu ile): " << priid1 << " " << priid2 << " " << priid3 << endl;
}
friend ostream &operator<<(ostream &stream, const sinif &nes);
friend istream &operator>>(istream &stream, sinif &nes);
};
ostream &operator<<(ostream &stream, const sinif &nes)
{
stream << "Değişken değerleri (<< fonksiyonu ile): " << nes.priid1 << " " << nes.priid2 << " " << nes.priid3 << endl;
return stream;
}
istream &operator>>(istream &stream, sinif &nes)
{
cout << "1.değişken değerini giriniz: ";
stream >> nes.priid1;
cout << "2.değişken değerini giriniz: ";
stream >> nes.priid2;
cout << "3.değişken değerini giriniz: ";
stream >> nes.priid3;
return stream;
}
int main(void)
{
sinif nes;
cin >> nes; // Değerlerin çoklu görev tanımlanmış >> işlemci fonksiyonu ile girilmesi
nes.deger_goster(); // Değerlerin sınıf fonksiyonu ile gösterilmesi
cout << nes; // Değerlerin çoklu görev tanımlanmış << işlemci fonksiyonu ile gösterilmesi
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
1.değişken değerini giriniz: 7 2.değişken değerini giriniz: 21 3.değişken değerini giriniz: 35 Değişken değerleri (Sınıf fonksiyonu ile): 7 21 35 Değişken değerleri (<< fonksiyonu ile): 7 21 35
Program, üç adet private int değer, bu int değerlere parametre almadan sıfır değeri atayan bir constructor fonksiyonu, birer int değer atayan parametre alan bir constructor fonksiyonu ve bu değerleri ekranda gösteren bir fonksiyon içeren bir sınıf oluşturur. Sonra, >> işlemcisine çoklu görev tanımlayarak, kendisine geçirilen nesne referansı yoluyla bir sınıf kopyası içindeki değişken değerlerine klavyeden değer alan friend bir fonksiyon oluşturur ve << işlemcisine çoklu görev tanımlayarak, kendisine geçirilen nesne yoluyla bir sınıf kopyası içindeki değişken değerlerini ekranda gösteren, friend bir fonksiyon oluşturur. Oluşturduğu sınıftan bir nesne bildirimi yapar. Nesne yoluyla, sınıf kopyası içinde yer alan değişken değerlerine çoklu görev tanımlama ile oluşturulan fonksiyon ile değer atar. Sonra, değişken değerlerini önce sınıf fonksiyonu ile sonra da çoklu görev tanımlama ile oluşturulan fonksiyon ile ekrana yazar.
C++'da, kendi manipülatör fonksiyonlarımızı oluşturabiliriz. Özel manipülatör fonksiyonlar oluşturmak, birden fazla giriş/çıkış işlemini tek bir fonksiyonda birleştirme ve giriş/çıkış işlemlerini daha pratik bir şekilde kodlamada fayda sağlar.
Özel manipülatör fonksiyonları giriş ve çıkış akışları için ayrı ayrı olmak üzere tanımlanır. Giriş ve çıkış manipülatör fonksiyonları için kullanılan genel yapı aşağıda gösterilmektedir:
ostream &manipülatör-adı(ostream &stream) { // Kod bloğu return stream; } istream &manipülatör-adı(istream &stream) { // Kod bloğu return stream; }
Şimdi, veri çıkış işlemleri için bir manipülatör fonksiyonun kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
// Özel manipülatör fonksiyonu
ostream &bg_hex(ostream &stream)
{
stream.setf(ios::showbase);
stream.setf(ios::hex, ios::basefield);
return stream;
}
int main(void)
{
int id = 21864;
cout << id << endl;
cout << bg_hex << id; // Özel manipülatör fonksiyonu kullanımı
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
21864 0x5568
Program, ios sınıfı içinde yer alan setf() fonksiyonunun iki farklı kullanımını tek bir fonksiyon içinde yaparak, çıkış işlemi için bir manipülatör fonksiyon oluşturur. Bildirimini yaptığı int bir değeri önce onluk sayı sisteminde sonra manipülatör fonksiyonunu kullanarak onaltılık sayı sisteminde ekrana yazar.
Şimdi, veri giriş ve çıkış işlemleri için manipülatör fonksiyonlarının kullanılmasını bir örnek üzerinde incelemeye çalışalım:
#include <iostream>
using namespace std;
// Giriş özel manipülatör fonksiyonu
istream °er_al(istream °er)
{
cout << "Bir değer giriniz: ";
return deger;
}
// Çıkış özel manipülatör fonksiyonu
ostream °er_goster(ostream °er)
{
deger << "Girilen değer: ";
return deger;
}
int main(void)
{
string str, id;
cin >> deger_al >> str;
cout << deger_goster << str;
cout << endl;
cin >> deger_al >> id;
cout << deger_goster << id;
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
Bir değer giriniz: Program Girilen değer: Program Bir değer giriniz: 21 Girilen değer: 21
Program, deger_al() isimli giriş manipülatör fonksiyonu ve deger_goster() isimli çıkış manipülatör fonksiyonu oluşturur. Bu fonksiyonları sırasıyla çalıştırarak önce bir karakter dizisi sonra int bir değer için işlem yapar ve girilen değerleri ekrana yazar.