You are on page 1of 23

Subscribe to DeepL Pro to translate larger documents.

826 Visit www.DeepL.com/pro


Bölüm 15: Akış for more information.
Sınıflarını Kullanarak
Giriş/Çıkış

Okuma sırasında dosyanın sonuna ulaşmadığınız sürece, aynı dosyaya okuma ve yazma
işlemleri arasında bir arama yapmak önemlidir. Bu arama yapılmazsa, muhtemelen bozuk bir
dosyayla veya daha da ölümcül hatalarla sonuçlanırsınız.
Daha önce de belirtildiği gibi, karakter karakter işlemek yerine, << işlecine argüman olarak
dosyanın akış arabelleğine bir işaretçi geçirerek tüm içeriği tek bir deyimde yazdırabilirsiniz
(ayrıntılar için bkz. Bölüm 15.14.3, sayfa 846):
std::cout << input.rdbuf();

15.13 Akış Tampon Sınıfları


Bölüm 15.2.1, sayfa 749'da belirtildiği gibi, okuma ve yazma doğrudan akışlar tarafından yapılmaz,
ancak akış tamponlarına devredilir.
Akış tamponlarıyla başa çıkmak için genel arayüz oldukça basittir (bkz. Bölüm 15.12.2, sayfa
820):
• rdbuf(), bir akışın akış tamponuna bir işaretçi verir.
- Akımların kurucusu ve rdbuf() işlevi, yapım sırasında bir akım tamponu ayarlamaya veya
akım mevcutken akım tamponunu değiştirmeye izin verir. Her iki durumda da, rdbuf()
işlevinin döndürdüğü akış tamponuna bir işaretçi aktarmanız gerekir.
Bu yetenek, akışların aynı çıkış aygıtına yazmasına izin vermek (bkz. Bölüm 15.12.2, sayfa 820,
akışları yeniden yönlendirmek (bkz. Bölüm 15.12.3, sayfa 822), aynı arabellekten okumak ve aynı
arabelleğe yazmak (bkz. Bölüm 15.12.4, sayfa 824) veya UTF-8 veya UTF-16/UCS-2 gibi diğer
karakter kodlamalarını giriş ve çıkış biçimi olarak kullanmak (bkz. Bölüm 16.4.4, sayfa 903) için
kullanılabilir.
Bu bölümde akış tampon sınıflarının nasıl çalıştığı açıklanmaktadır. Bu tartışma yalnızca G/Ç
akışları kullanıldığında neler olup bittiğinin daha iyi anlaşılmasını sağlamakla kalmaz, aynı
zamanda yeni G/Ç kanallarının tanımlanması için temel oluşturur. Akış tamponu işleminin
ayrıntılarına girmeden önce, yalnızca akış tamponlarını kullanmakla ilgilenen okuyucular için
genel arayüz sunulmuştur.

15.13.1 Akış Arabelleği Arabirimleri


Bir akış tamponu kullanıcısı için basic_streambuf<> sınıfı, karakterlerin gönderilebileceği
veya çıkarılabileceği bir şeyden çok daha fazlası değildir. Tablo 15.42 karakter yazmak için genel
fonksiyonları listeler.

Üye İşlevi Anlamı


sputc(c) c karakterini akış tamponuna gönderir
sputn(s, n) s dizisinden n karakteri akış tamponuna gönderir

Tablo 15.42. Karakter Yazmak için Genel Üyeler

sputc() fonksiyonu hata durumunda traits_type::eof() döndürür, burada traits_type


basic_streambuf sınıfında bir tür tanımıdır. sputn() işlevi, aşağıdaki sayıları yazar
15.13 Dere Tampon Sınıfları 827

Akış arabelleği bunları tüketemediği sürece ikinci bağımsız değişken tarafından belirtilen
karakterler. (Sonlandırıcı) null karakterleri önemsemez. Bu fonksiyon yazılan karakter sayısını
döndürür. Akış tamponundan karakter okuma arayüzü biraz daha karmaşıktır (Tablo 15.43) çünkü
girdi için, bir karakteri tüketmeden ona bakmak gerekir. Ayrıca, ayrıştırma sırasında karakterlerin
akış tamponuna geri konulabilmesi istenir. Böylece, akış
tampon sınıfları ilgili işlevleri sağlar.

Üye İşlevi Anlamı


in_avail() Mevcut karakterler için bir alt sınır döndürür Geçerli
sgetc() karakteri tüketmeden döndürür Geçerli karakteri
sbumpc() döndürür ve tüketir
snextc() Geçerli karakteri alır ve sonraki karakteri döndürür n karakter
sgetn(b, n) okur ve bunları arabellekte saklar b
sputbackc(c) Akış tamponuna c karakterini döndürür
sungetc() Önceki karaktere bir adım geri gider

Tablo 15.43. Karakterleri Okumak için Genel Üyeler

in_avail() fonksiyonu en az kaç karakterin kullanılabilir olduğunu belirlemek için


kullanılabilir. Bu fonksiyon, örneğin klavyeden okuma yaparken okumanın engellenmediğinden
emin olmak için kullanılabilir. Ancak, daha fazla karakter kullanılabilir.
Akış tamponu akışın sonuna ulaşana kadar geçerli bir karakter vardır. sgetc() fonksiyonu,
bir sonraki karaktere geçmeden mevcut karakteri almak için kullanılır. sbumpc() fonksiyonu
mevcut karakteri okur ve bir sonraki karaktere geçerek bunu yeni mevcut karakter haline getirir.
Tek bir karakteri okuyan son fonksiyon snextc() bir sonraki karakteri geçerli karakter yapar ve
sonra bu karakteri okur. Her üç fonksiyon da başarısızlığı belirtmek için traits_type::eof()
döndürür. sgetn() fonksiyonu bir karakter dizisini bir tampona okur. En fazla
okunacak karakter sayısı bir argüman olarak aktarılır. Fonksiyon okunan karakter sayısını
döndürür.
İki fonksiyon sputbackc() ve sungetc() bir adım geriye gitmek için kullanılır ve önceki
karakteri geçerli karakter yapar. sputbackc() fonksiyonu önceki karakteri başka bir karakterle
değiştirmek için kullanılabilir. Bu iki fonksiyon sadece dikkatle kullanılmalıdır. Genellikle,
yalnızca bir karakter geri alınabilir.
Son olarak, gömülü yerel ayar nesnesine erişmek, konumu değiştirmek ve tamponlamayı
etkilemek için işlevler vardır. Tablo 15.44 bu fonksiyonları listeler.
Hem pubimbue() hem de getloc() uluslararasılaştırma için kullanılır (bkz. Bölüm 15.8,
sayfa 790): pubimbue() akış tamponuna yeni bir yerel ayar nesnesi yükler ve önceden yüklenmiş
yerel ayar nesnesini döndürür; getloc() geçerli olarak yüklenmiş yerel ayar nesnesini döndürür.
pubsetbuf() işlevi, akış tamponlarının tamponlama stratejisi üzerinde bir miktar kontrol
sağlamayı amaçlamaktadır. Bununla birlikte, bunun yerine getirilip getirilmeyeceği somut akış
tamponu sınıfına bağlıdır. Örneğin, dize akış tamponları için pubsetbuf() işlevini kullanmak
mantıklı değildir. Dosya akış tamponları için bile, bu işlevin kullanımı yalnızca ilk G/Ç işlemi
gerçekleştirilmeden önce çağrılırsa taşınabilir ve
828 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

Üye İşlevi Anlamı


pubimbue(loc) Akış tamponuna loc yerel ayarını yükler Geçerli
getloc() yerel ayarı döndürür
pubseekpos(pos) Geçerli konumu mutlak bir konuma yeniden
pubseekpos(pos, which) konumlandırır G/Ç yönünün belirtilmesiyle aynı
pubseekoff(offset, rpos) Geçerli konumu başka bir konuma göre yeniden
konumlandırır
pubseekoff(offset, rpos, which) G/Ç yönünün belirtilmesi ile aynı
pubsetbuf(buf, n) Tamponlamayı etkiler

Tablo 15.44. Çeşitli Genel Akış Tampon İşlevleri

pubsetbuf(nullptr,0) olarak çağrılırsa, bu hiçbir tamponun kullanılmayacağı anlamına gelir.


Bu fonksiyon başarısızlık durumunda nullptr döndürür, aksi takdirde akış tamponunu döndürür.
pubseekoff() ve pubseekpos() fonksiyonları okuma ve/veya yazma için kullanılan
geçerli konumu değiştirmek için kullanılır. Değiştirilen konum, ios_base::openmode türünde
olan ve belirtilmemişse varsayılan olarak ios_base::in|ios_base::out olan son bağımsız
değişkene bağlıdır. Eğer ios_base::in ayarlanırsa, okuma konumu değiştirilir. Buna karşılık,
ios_base::out ayarlanırsa yazma konumu değiştirilir. pubseekpos() işlevi akışı ilk bağımsız
değişken olarak belirtilen mutlak bir konuma taşırken, pubseekoff() işlevi akışı başka bir
konuma göreli olarak taşır. Ofset ilk bağımsız değişken olarak belirtilir. Başlangıç noktası olarak
kullanılan konum ikinci bağımsız değişken olarak belirtilir ve ios_base::cur, ios_base::beg
veya ios_base::end olabilir (ayrıntılar için bkz. Bölüm 15.9.4, sayfa 800). Her iki fonksiyon da
akışın konumlandırıldığı pozisyonu ya da geçersiz bir akış pozisyonu döndürür. Geçersiz akım
konumu, sonucu pos_type(off_type(-1)) nesnesiyle karşılaştırarak tespit edilebilir (pos_type
ve off_type akım konumlarını işlemek için kullanılan türlerdir; bakınız Bölüm 15.9.4, sayfa
799). Bir akımın geçerli konumu pubseekoff() kullanılarak elde edilebilir:
sbuf.pubseekoff(0, std::ios::cur)

15.13.2 Akış Arabelleği Yineleyicileri


Biçimlendirilmemiş G/Ç için üye işlev kullanmanın alternatif bir yolu da akış arabellek yineleyici
sınıflarını kullanmaktır. Bu sınıflar, giriş yineleyici veya çıkış yineleyici gereksinimlerine uyan ve
akış tamponlarından tek tek karakterleri okuyan veya yazan yineleyiciler sağlar. Bu, karakter
düzeyinde G/Ç'yi C++ standart kütüphanesinin algoritma kütüphanesine sığdırır.
istreambuf_iterator<> ve ostreambuf_iterator<> sınıf şablonları sırasıyla
basic_streambuf<> türündeki nesnelerden tek tek karakterleri okumak ya da bu nesnelere
yazmak için kullanılır. Sınıflar <iterator> başlığında aşağıdaki gibi tanımlanır:
namespace std {
şablon <tpename charT,
typename traits = char_traits<charT> >
class istreambuf_iterator;
15.13 Dere Tampon Sınıfları 829

şablon <tpename charT,


typename traits = char_traits<charT> >
class ostreambuf_iterator;
}
Bu yineleyiciler, Bölüm 9.4.3, sayfa 460'da açıklanan akış yineleyicilerinin özel biçimleridir.
Aralarındaki tek fark, elemanlarının karakter olmasıdır.

Çıkış Akışı Tampon Yineleyicileri

Bir dizenin bir ostreambuf_iterator kullanılarak bir akış tamponuna nasıl yazılabileceği aşağıda
açıklanmıştır:
// çıktı akışının tamponu için yineleyici oluşturun cout
std::ostreambuf_iterator<char> bufWriter(std::cout);

std::string hello("hello, world\n");


std::copy(hello.begin(), hello.end(), // kaynak: string
bufWriter); // hedef: cout'un çıktı tamponu
Bu örneğin ilk satırı, cout nesnesinden ostreambuf_iterator türünde bir çıktı yineleyicisi
oluşturur. Çıktı akışını geçirmek yerine, doğrudan akış tamponuna bir işaretçi de geçirebilirsiniz.
Kalan kısım bir string nesnesi oluşturur ve bu nesnedeki karakterleri oluşturulan çıktı
yineleyicisine kopyalar.
Tablo 15.45 çıktı akışı tampon yineleyicilerinin tüm işlemlerini listeler. Uygulama ostream
yineleyicilerine benzer (bkz. Bölüm 9.4.3, sayfa 460). Ek olarak, yineleyiciyi bir tamponla
başlatabilir ve yineleyicinin yazıp yazamayacağını sorgulamak için failed() işlevini
çağırabilirsiniz. Daha önce bir karakterin yazılması başarısız olduysa, failed() true
sonucunu verir. Bu durumda, operator = ile herhangi bir yazmanın etkisi yoktur.

İfade Etki
ostreambuf_iterator<char>(ostream) için bir çıktı akışı tampon yineleyicisi
oluşturur.
ostreambuf_iterator<char>(buffer_ptr) ostream
buffer_ptr'nin başvurduğu tampon için bir
çıkış akışı tampon yineleyicisi oluşturur
*iter No-op (iter döndürür)
iter = c Çağırarak tampona c karakterini yazar
bunun için sputc(c)
++iter No-op (iter döndürür)
iter++ No-op (iter döndürür)
failed() Çıktı akışı yineleyicisinin artık yazıp
yazamayacağını döndürür

Tablo 15.45. Çıkış Akışı Tampon Yineleyicilerinin İşlemleri


830 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

Giriş Akışı Arabelleği Yineleyicileri

Tablo 15.46 giriş akışı tampon yineleyicilerinin tüm işlemlerini listeler. Uygulama, akış
yineleyicilerine benzerdir (bkz. Bölüm 9.4.3, sayfa 462). Ayrıca, yineleyiciyi bir tamponla
başlatabilirsiniz ve iki giriş akışı tampon yineleyicisinin eşit olup olmadığını döndüren equal()
üye işlevi sağlanır. İki giriş akışı tampon yineleyicisi, her ikisi de akış sonu yineleyicisi olduğunda
veya hiçbiri akış sonu yineleyicisi olmadığında eşittir.

İfade Etki
istreambuf_iterator<char>() Akış sonu yineleyicisi oluşturur
istreambuf_iterator<char>(istream) istream için bir giriş akışı tampon
yineleyicisi oluşturur ve sgetc()
işlevini kullanarak ilk karakteri
istreambuf_iterator<char>(buffer_ptr) okuyabilir
buffer_ptr öğesinin başvurduğu arabellek
için bir giriş akışı arabellek yineleyicisi
*iter oluşturur ve sgetc() işlevini kullanarak
ilk karakteri okuyabilir
Daha önce sgetc() ile okunan geçerli
++it karakteri döndürür (kurucu tarafından
yapılmadıysa ilk karakteri okur)
er Sonraki karakteri sbumpc() ile okur ve
konumunu döndürür
iter Sonraki karakteri sbumpc() ile okur, ancak *
önceki karakteri veren bir yineleyici (proxy)
++ döndürür
Her iki yineleyicinin eşit olup olmadığını
döndürür Eşitlik için iter1 ve iter2'yi test
iter1.equal(iter2) eder
iter1== iter2 Eşitsizlik için iter1 ve iter2'yi test eder
iter1!= iter2

Tablo 15.46. Giriş Akışı Tampon Yineleyicilerinin İşlemleri

İstreambuf_iterator tipindeki iki nesnenin eşdeğer olmasının ne anlama geldiği biraz


belirsizdir: İki istreambuf_iterator nesnesi, her iki yineleyici de akış sonu yineleyicisi ise
veya hiçbiri akış sonu yineleyicisi değilse eşdeğerdir (çıktı tamponunun aynı olup olmaması
önemli değildir). Akış sonu yineleyicisi elde etmek için bir olasılık, varsayılan yapıcı ile bir
yineleyici oluşturmaktır. Buna ek olarak, bir istreambuf_iterator, yineleyiciyi akışın
sonundan ilerletmek için bir at- tempt yapıldığında (başka bir deyişle, sbumpc()
traits_type::eof() döndürürse) bir akış sonu yineleyicisi haline gelir. Bu davranışın iki
önemli sonucu vardır:
1. Bir akımdaki geçerli konumdan akımın sonuna kadar olan bir aralık iki yineleyici ile tanımlanır:
geçerli konum için istreambuf_iterator<charT,traits>(akım) ve akımın sonu için
istreambuf_iterator<charT,traits>() (akım basic_istream<charT,traits>
veya basic_streambuf<charT,traits> türündedir).
2. istreambuf_iterators kullanarak alt aralıklar oluşturmak mümkün değildir.
15.13 Dere Tampon Sınıfları 831

Akış Arabelleği Yineleyicilerinin Örnek Kullanımı

Aşağıdaki örnek, okunan tüm karakterleri akış tampon yineleyicileri ile yazan klasik filtre
çerçevesidir. Bölüm 15.5.3, sayfa 772'deki örneğin değiştirilmiş bir versiyonudur:
// io/charcat2.cpp
#include <iostream>
#include <iterator>
using namespace
std;

int main()
{
// cin için giriş akışı tampon yineleyicisi
istreambuf_iterator<char> inpos(cin);

// akış sonu yineleyicisi


istreambuf_iterator<char> endpos;

// cout için çıktı akışı tampon yineleyicisi


ostreambuf_iterator<char> outpos(cout);

// giriş yineleyicisi geçerli iken


while (inpos != endpos) {
*outpos = *inpos; // değerini çıktı yineleyicisine ata
++inpos;
++outpos;
}
}

Ayrıca bir girdi akışından okunan tüm karakterleri işlemek için algoritmalara akış tampon
yineleyicileri de aktarabilirsiniz (tam bir örnek için io/countlines1.cpp dosyasına bakın):
int countLines (std::istream& in)
{
return std::count(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>(),
'\n');
}
Bir dizeyi başlatmak için standart girdiden okunan tüm karakterleri kullanan bir örnek için Bölüm
14.6, sayfa 732'ye bakın.
832 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

15.13.3 Kullanıcı Tanımlı Akış Tamponları


Akış tamponları G/Ç için tamponlardır. Arayüzleri basic_streambuf<> sınıfı tarafından
tanımlanır. Karakter tipleri char ve wchar_t için, sırasıyla streambuf ve wstreambuf
özelleşmeleri önceden tanımlanmıştır. Bu sınıflar, özel G/Ç kanalları üzerinden iletişim
gerçekleştirilirken temel sınıflar olarak kullanılır. Ancak, bunu yapmak akış tamponunun
çalışmasını anlamayı gerektirir.
Tamponlara giden merkezi arayüz, iki tamponun her biri için üç işaretçi tarafından oluşturulur.
eback(), gptr() ve egptr() fonksiyonlarından dönen işaretçiler okuma tamponunun
arayüzünü oluşturur. pbase(), pptr() ve epptr() fonksiyonlarından dönen işaretçiler yazma
tamponunun arayüzünü oluşturur. Bu işaretçiler okuma ve yazma işlemleri tarafından manipüle
edilir, bu da ilgili okuma veya yazma kanalında karşılık gelen reaksiyonlarla sonuçlanabilir. Tam
işlem okuma ve yazma için ayrı ayrı incelenir.

Kullanıcı Tanımlı Çıkış Tamponları

Karakter yazmak için kullanılan bir tampon, pbase(), pptr() ve epptr() fonksiyonları
tarafından erişilebilen üç işaretçi ile tutulur (Şekil 15.4). İşte bu işaretçiler neyi temsil eder:
1. pbase() ("put base") çıktı tamponunun başlangıcıdır.
2. pptr() ("put pointer") geçerli yazma konumudur.
3. epptr() ("end put pointer") çıktı tamponunun sonudur. Bu, epptr()'nin tamponlanabilecek
son karakterden bir sonrasına işaret ettiği anlamına gelir.
tarafından işaret edilen karakter hariç, pbase() ile pptr() arasındaki aralıktaki karakterler
pptr(), zaten yazılmış ancak henüz ilgili çıkış kanalına aktarılmamış veya temizlenmemiştir.

pbase() pptr() epptr()

Şekil 15.4. Çıkış Tamponuna Arayüz

sputc() üye fonksiyonu kullanılarak bir karakter yazılır. Bu karakter, boş bir yazma konumu
varsa geçerli yazma konumuna kopyalanır. Daha sonra geçerli yazma konumunun göstericisi
artırılır. Tampon doluysa (pptr() == epptr()), çıkış tamponunun içeriği overflow() sanal
fonksiyonu çağrılarak ilgili çıkış kanalına gönderilir. Bu fonksiyon karakterleri, dize akışlarında
olduğu gibi dahili olabilen bazı "harici temsillere" göndermekten sorumludur. Temel
basic_streambuf sınıfındaki overflow() uygulaması sadece dosya sonu döndürür, bu da daha
fazla karakter yazılamayacağını gösterir.
sputn() üye fonksiyonu aynı anda birden fazla karakter yazmak için kullanılabilir. Bu
fonksiyon, işi daha verimli bir şekilde gerçekleştirilebilen xsputn() sanal fonksiyonuna
devreder.
15.13 Dere Tampon Sınıfları 833

birden fazla karakterin yazılması. basic_streambuf sınıfındaki xsputn() uygulaması her


karakter için sputc() işlevini çağırır. Bu nedenle, xsputn() işlevinin geçersiz kılınması
gerekli değildir. Ancak genellikle, birden fazla karakter yazmak, karakterleri teker teker
yazmaktan daha verimli bir şekilde uygulanabilir. Dolayısıyla, bu fonksiyon karakter dizilerinin
işlenmesini optimize etmek için kullanılabilir.
Bir akış tamponuna yazmak için tamponun kullanılması gerekmez. Bunun yerine, karakterler
alınır alınmaz yazılabilir. Bu durumda, yazma tamponunu tutan işaretçilere nullptr (veya 0 veya
NULL) değeri atanmalıdır. Varsayılan kurucu bunu otomatik olarak yapar.
Bu bilgilerle, basit bir akış tamponunun aşağıdaki örneği uygulanabilir. Bu akış tamponu bir
tampon kullanmaz. Bu nedenle, her karakter için overflow() işlevi çağrılır. Gerekli olan tek şey
bu fonksiyonun uygulanmasıdır:
// io/outbuf1.hpp
#include <streambuf>
#include <locale>
#include <cstdio>

class outbuf : public std::streambuf


{
korumalı:
// merkezi çıktı işlevi
// - karakterleri büyük harf modunda yazdırır
virtual int_type overflow (int_type c) {
if (c != EOF) {
// küçük harfi büyük harfe dönüştür
c = std::toupper(c,getloc());

// ve karakteri standart çıktıya yazın


if (std::putchar(c) == EOF) {
return EOF;
}
}
c'yi döndür;
}
};

Bu durumda, akış tamponuna gönderilen her karakter putchar() C fonksiyonu kullanılarak


yazılır. Ancak, karakter yazılmadan önce toupper() kullanılarak büyük harf karakterine
dönüştürülür (bkz. Bölüm 16.4.4, sayfa 895). getloc() işlevi, akış tamponuyla ilişkili yerel ayar
nesnesini almak için kullanılır (ayrıca bkz. Bölüm 15.8, sayfa 790).
Bu örnekte, çıkış tamponu özellikle char karakter türü için uygulanmıştır (streambuf,
char karakter türü için basic_streambuf<>'un özelleşmesidir). Diğer karakter türleri
kullanılıyorsa, bu işlevi karakter özelliklerini kullanarak gerçekleştirmeniz gerekir, bunlar
834 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

Bölüm 16.1.4, sayfa 853'te tanıtılmıştır. Bu durumda, c'nin dosya sonu ile karşılaştırılması farklı
görünür: EOF yerine traits::eof() döndürülmelidir ve c argümanı EOF ise,
traits::not_eof(c) değeri döndürülmelidir, burada traits basic_streambuf'un ikinci şablon
argümanıdır. Bu aşağıdaki gibi görünebilir:
// io/outbuf1i18n.hpp
#include <streambuf>
#include <locale>
#include <cstdio>

şablon <tpename charT,


typename traits = std::char_traits<charT> >
class basic_outbuf : public std::basic_streambuf<charT,traits>
{
korumalı:
// merkezi çıktı işlevi
// - karakterleri büyük harf modunda yazdırır
virtual typename traits::int_type
taşma (typename traits::int_type c) { if
(!traits::eq_int_type(c,traits::eof())) {
// küçük harfi büyük harfe dönüştür
c = std::toupper(c,this->getloc());

// karakteri bir karaktere dönüştürün (varsayılan: '?')


char cc = std::use_facet<std::ctype<charT>>
(this->getloc()).narrow(c,'?');

// ve karakteri standart çıktıya yazın


if (std::putchar(cc) == EOF) {
return traits::eof();
}
}
return traits::not_eof(c);
}
};

typedef basic_outbuf<char>outbuf
;
typedef basic_outbuf<wchar_t> woutbuf;

Temel sınıf bir şablon parametresine bağlı olduğu için getloc() çağrısını artık this-> ile
nitelemeniz gerektiğini unutmayın. Ayrıca, putchar() işlevine aktarmadan önce karakteri
daraltmamız gerekir, çünkü putchar() yalnızca char kabul eder (bkz. Bölüm 16.4.4, sayfa
891).
15.13 Dere Tampon Sınıfları 835

Bu akış tamponunu aşağıdaki programda kullanın:


// io/outbuf1.cpp
#include <iostream>
#include "outbuf1.hpp"

int main()
{
outbuf ob; // özel çıktı tamponu oluştur
std::ostream out(&ob); // çıktı akışını bu çıktı tamponu ile başlat

out << "31 hexadecimal: " << std::hex << 31 << std::endl;
}

aşağıdaki çıktıyı üretir:


31 ONALTILIK: 1F
Aynı yaklaşım diğer keyfi hedeflere yazmak için de kullanılabilir. Örneğin, bir akış tamponunun
kurucusu, bir dosya tanımlayıcısını, bir soket bağlantısının adını veya nesneyi başlatmak için
eşzamanlı yazma için kullanılan diğer iki akış tamponunu alabilir. İlgili hedefe yazmak için
yalnızca overflow() işlevinin uygulanması gerekir. Buna ek olarak, akış tamponuna
yazmayı daha verimli hale getirmek için xsputn() işlevi uygulanmalıdır.
Akış tamponunun uygun şekilde oluşturulması için, esas olarak kurucu argümanını ilgili akış
tamponuna geçiren özel bir akış sınıfının uygulanması da mantıklıdır. Bir sonraki örnek bunu
göstermektedir. Bir dosya tanımlayıcısı ile başlatılan bir akış tampon sınıfı tanımlar
UNIX benzeri işletim sistemlerinde kullanılan düşük seviyeli bir G/Ç işlevi olan write() işlevi
ile hangi karakterlerin yazıldığı. Ayrıca, dosya tanımlayıcısının geçirildiği böyle bir akış
tamponunu koruyan ostream'den türetilmiş bir sınıf tanımlanmıştır:
// io/outbuf2.hpp
#include <iostream>
#include <streambuf>
#include <cstdio>

// write() için:
#ifdef
_MSC_VER
#include
<io.h> #else
#include <unistd.h>
#endif

class fdoutbuf : public std::streambuf {


protected:
int fd; // dosya tanımlayıcısı
836 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

Halka açık:
// kurucu
fdoutbuf (int _fd) : fd(_fd) {
}
korumalı:
// bir karakter yaz
virtual int_type overflow (int_type c) {
if (c != EOF) {
char z = c;
if (write (fd, &z, 1) != 1) {
return EOF;
}
}
c'yi döndür;
}
// birden fazla karakter yaz
virtual std::streamsize xsputn (const char* s,
std::streamsize num) {
return write(fd,s,num);
}
};

class fdostream : public std::ostream {


protected:
fdoutbuf
buf; public:
fdostream (int fd) : std::ostream(0), buf(fd) {
rdbuf(&buf);
}
};

Bu akış tamponuna bir karakter dizisi gönderilirse, her karakter için overflow() işlevinin
çağrılmasını önlemek için bu akış tamponu xsputn() işlevini de uygular. Bu fonksiyon, fd
dosya tanımlayıcısı tarafından tanımlanan dosyaya tek bir çağrı ile tüm karakter dizisini yazar.
xsputn() fonksiyonu başarıyla yazılan karakter sayısını döndürür. İşte örnek bir uygulama:
// io/outbuf2.cpp
#include <iostream>
#include "outbuf2.hpp"
15.13 Dere Tampon Sınıfları 837

int main()
{
fdostream out(1); // dosya tanımlayıcısı 1'e yazan tamponlu akış

out << "31 hexadecimal: " << std::hex << 31 << std::endl;
}

Bu program, dosya tanımlayıcısı 1 ile başlatılan bir çıkış akışı oluşturur. Bu dosya tanımlayıcısı,
geleneksel olarak, standart çıkış kanalını tanımlar. Dolayısıyla, bu örnekte karakterler basitçe
yazdırılır. Başka bir dosya tanımlayıcısı mevcutsa - örneğin, bir dosya veya soket için - bu da
kurucu argüman olarak kullanılabilir.
Tamponlama yapan bir akış tamponu uygulamak için, yazma tamponunun aşağıdaki işlev
kullanılarak başlatılması gerekir
setp(). Bu, bir sonraki örnekte gösterilmektedir:
// io/outbuf3.hpp
#include <cstdio>
#include <streambuf>

// write() için:
#ifdef _MSC_VER
# include
<io.h> #else
# include <unistd.h>
#endif

class outbuf : public std::streambuf {


protected:
static const int bufferSize = 10; // veri tamponunun boyutu
char buffer[bufferSize]; // veri tamponu

Halka açık:
// kurucu
// - veri arabelleğini başlat
// - bufferSizeth karakterinin overflow() çağrısına neden olmasına izin vermek için bir
karakter daha az outbuf() {
setp (buffer, buffer+(bufferSize-1));
}

// yıkıcı
// - veri tamponunu yıka
virtual ~outbuf() {
sync();
}
838 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

korumalı:
// tampondaki karakterleri temizle
int flushBuffer () {
int num = pptr()-pbase();
if (write (1, buffer, num) != num) {
return EOF;
}
pbump (-num); // put işaretçisini uygun şekilde sıfırla
return num;
}

// arabellek dolu
// - c ve önceki tüm karakterleri yaz
virtual int_type overflow (int_type c) {
if (c != EOF) {
// tampona karakter ekle
*pptr() = c;
pbump(1);
}
// tamponu temizle
if (flushBuffer() == EOF) {
// HATA
EOF döndür;
}
c'yi döndür;
}

// verileri dosya/hedef ile senkronize et


// - tampondaki verileri temizleyin
virtual int sync () {
if (flushBuffer() == EOF) {
// HATA
-1 döndür;
}
0 döndür;
}
};

Kurucu, yazma tamponunu setp() ile başlatır:


setp (tampon, tampon+(boyut-1));
Yazma tamponu, hala bir karakterlik yer varken overflow() çağrılacak şekilde ayarlanır. Eğer
overflow() argüman olarak EOF ile çağrılmazsa, ilgili karakter
15.13 Dere Tampon Sınıfları 839

yazma konumuna yazılabilir, çünkü yazma konumunun işaretçisi bitiş işaretçisinin ötesine
artırılmaz. overflow() argümanı yazma konumuna yerleştirildikten sonra, tüm tampon
boşaltılabilir.
flushBuffer() üye fonksiyonu tam olarak bunu yapar. Karakterleri write() fonksiyonunu
kullanarak standart çıkış kanalına (dosya tanımlayıcı 1) yazar. Akış tamponunun üye işlevi
pbump(), yazma konumunu tamponun başına geri taşımak için kullanılır.
overflow() işlevi, EOF değilse overflow() çağrısına neden olan karakteri tampona ekler.
Daha sonra, pbump() işlevi yazma konumunu tampon karakterlerin yeni sonunu yansıtacak
şekilde ilerletmek için kullanılır. Bu, yazma konumunu geçici olarak bitiş konumunun (epptr())
ötesine taşır.
Bu sınıf ayrıca, akış tamponunun mevcut durumunu ilgili depolama ortamıyla senkronize
etmek için kullanılan sync() sanal fonksiyonuna sahiptir. Normalde, yapılması gereken tek şey
tamponu temizlemektir. Akış tamponunun tamponlanmamış versiyonları için, bu fonksiyonun
geçersiz kılınması gerekli değildi, çünkü temizlenecek bir tampon yoktu.
Sanal yıkıcı, akış arabelleği yok edildiğinde hala arabelleğe alınmış olan verilerin yazılmasını
sağlar.
Bunlar çoğu akış tamponu için geçersiz kılınan işlevlerdir. Harici gösterimin bazı özel yapıları
varsa, ek fonksiyonların geçersiz kılınması faydalı olabilir. Örneğin, seekoff() ve seekpos()
fonksiyonları yazma pozisyonunun manipülasyonuna izin vermek için geçersiz kılınabilir.

Kullanıcı Tanımlı Giriş Tamponları

Girdi mekanizması temelde çıktı mekanizması ile aynı şekilde çalışır. Ancak, girdi için son
okumayı geri alma imkanı da vardır. Girdi akışının unget() işlevi tarafından çağrılan sungetc()
veya girdi akışının putback() işlevi tarafından çağrılan sputbackc() işlevleri, akış tamponunu
son okumadan önceki durumuna yeniden depolamak için kullanılabilir. Okuma konumunu bu
karakterin ötesine taşımadan bir sonraki karakteri okumak da mümkündür. Bu nedenle, bir akış
tamponundan okumayı uygulamak için, bir akış tamponuna yazmayı uygulamak için gerekenden
daha fazla işlevi geçersiz kılmanız gerekir.
Bir akış tamponu, eback(), gptr() ve egptr() üye fonksiyonları aracılığıyla erişilebilen
üç işaretçi içeren bir okuma tamponu tutar (Şekil 15.5):
1. eback() ("end back") giriş tamponunun başlangıcı ya da adından da anlaşılacağı gibi geri
koyma alanının sonudur. Karakter, özel bir işlem yapılmadan yalnızca bu konuma kadar geri
konulabilir.
2. gptr() ("get pointer") geçerli okuma konumudur.
3. egptr() ("end get pointer") girdi tamponunun sonudur.
Okuma konumu ile son konum arasındaki karakterler harici gösterimden programın belleğine
taşınmıştır, ancak hala program tarafından işlenmeyi beklemektedirler.
Tek karakterler sgetc() veya sbumpc() fonksiyonları kullanılarak okunabilir. Bu iki
fonksiyonun farkı, okuma işaretçisinin sgetc() tarafından değil, sbumpc() tarafından
artırılmasıdır. Tampon tamamen okunmuşsa (gptr() == egptr()), hiçbir karakter mevcut
değildir ve tampon, verilerin okunmasından sorumlu olan underflow() sanal fonksiyonunun
çağrılmasıyla yeniden doldurulmalıdır. Hiçbir karakter mevcut değilse, sbumpc() işlevi bunun
yerine uflow() sanal işlevini çağırır. Bu durumda
840 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

eback() gptr() egptr()

Şekil 15.5. Akış Tamponlarından Okuma için Arayüz

uflow() işlevinin varsayılan uygulaması underflow() işlevini çağırmak ve ardından


okuma işaretçisini artırmaktır. Temel basic_streambuf sınıfındaki underflow() işlevinin
varsayılan uygulaması EOF döndürmektir. Bu, varsayılan uygulama ile karakterleri okumanın
imkansız olduğu anlamına gelir.
sgetn() işlevi aynı anda birden fazla karakter okumak için kullanılır. Bu fonksiyon işlemeyi
xsgetn() sanal fonksiyonuna devreder. xsgetn() işlevinin varsayılan uygulaması, her
karakter için sbumpc() işlevini çağırarak birden fazla karakteri çıkarır. Yazma için xsputn()
işlevi gibi, xsgetn() işlevi de birden fazla karakterin okunmasını optimize etmek
için uygulanabilir.
Giriş için, çıkış durumunda olduğu gibi sadece bir fonksiyonu geçersiz kılmak yeterli değildir.
Ya bir tampon oluşturulmalı ya da en azından underflow() ve uflow() uygulanmalıdır. Bunun
nedeni, underflow() işlevinin mevcut karakterin ötesine geçmemesidir, ancak
underflow() işlevi sgetc() işlevinden çağrılabilir. Bir sonraki karaktere geçmek için
tampon manipülasyonu veya uflow() çağrısı kullanılmalıdır. Her durumda, underflow()
karakterleri okuyabilen herhangi bir akış tamponu için uygulanmalıdır. Hem underflow() hem de
uflow() uygulanmışsa, bir tampon oluşturmaya gerek yoktur.
Bir okuma tamponu, bu sırada üç bağımsız değişken alan setg() üye işlevi ile ayarlanır:
1. Arabelleğin başlangıcına bir işaretçi (eback())
2. Geçerli okuma konumuna bir işaretçi (gptr())
3. Arabelleğin sonuna bir işaretçi (egptr())
setp()'den farklı olarak setg(), akışa geri konan karakterlerin saklanacağı alanı
tanımlayabilmek için üç argüman alır. Bu nedenle, okuma tamponuna işaretçiler ayarlanırken,
zaten okunmuş ancak hala tamponda saklanan en az bir karakterin olması mantıklıdır.
Daha önce de belirtildiği gibi, sputbackc() ve sungetc() fonksiyonları kullanılarak
karakterler okuma tamponuna geri konulabilir. sputbackc() fonksiyonu argüman olarak geri
konulacak karakteri alır ve bu karakterin gerçekten okunan karakter olduğundan emin olur. Her iki
fonksiyon da mümkünse okuma işaretçisini azaltır. Elbette, bu yalnızca okuma işaretçisi okuma
tamponunun başında olmadığı sürece çalışır. Tamponun başlangıcına ulaşıldıktan sonra bir
karakteri geri koymaya çalışırsanız, pbackfail() sanal fonksiyonu çağrılır. Bu fonksiyonu
geçersiz kılarak, bu durumda bile eski okuma konumunu yeniden saklamak için bir mekanizma
uygulayabilirsiniz. basic_streambuf temel sınıfında buna karşılık gelen bir davranış
tanımlanmamıştır. Bu nedenle, pratikte, keyfi sayıda karakter geriye gitmek mümkün değildir.
Tampon kullanmayan akışlar için pbackfail() işlevi uygulanmalıdır, çünkü genellikle en az bir
karakterin akışa geri konulabileceği varsayılır.
15.13 Dere Tampon Sınıfları 841

Yeni bir tampon okunmuşsa, başka bir sorun ortaya çıkar: Eski veri tampona kaydedilmezse bir
karakter bile geri konulamaz. Bu nedenle, underflow() uygulaması genellikle geçerli tamponun
son birkaç karakterini (örneğin dört karakter) tamponun başına taşır ve ardından yeni okunan
karakterleri ekler. Bu, pbackfail() çağrılmadan önce bazı karakterlerin geri taşınmasını sağlar.
Aşağıdaki örnek, böyle bir uygulamanın nasıl görünebileceğini göstermektedir. inbuf
sınıfında, on karakterli bir giriş tamponu uygulanmıştır. Bu tampon, geri koyma alanı için en fazla
dört karaktere ve "normal" giriş tamponu için altı karaktere bölünmüştür:
// io/inbuf1.hpp
#include <cstdio>
#include <cstring>
#include <streambuf>

// read() için:
#ifdef _MSC_VER
# include
<io.h> #else
# include <unistd.h>
#endif

class inbuf : public std::streambuf {

protected:
// veri tamponu:
// - geri koyma alanında en fazla dört karakter artı
// - sıradan okuma tamponunda en fazla altı karakter
static const int bufferSize = 10; // veri tamponunun boyutu
char buffer[bufferSize]; // veri tamponu

Halka açık:
// kurucu
// - boş veri arabelleğini başlat
// - geri koyma alanı yok
// => force underflow()
inbuf() {
setg (buffer+4, // geri koyma alanının
başlangıcı buffer+4, // okuma
pozisyonu tampon+4); // son konum
}
842 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

korumalı:
// tampona yeni karakterler ekle
virtual int_type underflow () {
// okunan konum arabellek sonundan önce mi?
if (gptr() < egptr()) {
return traits_type::to_int_type(*gptr());
}

// geri koyma alanının işlem boyutu


// - okunan karakter sayısını kullanın
// - ama en fazla dört
int numPutback;
numPutback = gptr() -
eback(); if (numPutback > 4)
{
numPutback = 4;
}

// önceden okunan en fazla dört karakteri kopyalayın


// geri koyma tamponu (ilk dört karakterlik alan)
std::memmove (buffer+(4-numPutback), gptr()-numPutback,
numPutback);

// yeni karakterleri oku


int num;
num = read (0, buffer+4, bufferSize-4);
if (num <= 0) {
// ERROR veya EOF
EOF döndür;
}

// tampon işaretçilerini sıfırla


setg (tampon+(4-numPutback), // geri koyma alanının başlangıcı
tampon+4, // okuma konumu
tampon+4+num); // tamponun sonu

// sonraki karakteri döndür


return traits_type::to_int_type(*gptr());
}
};

Kurucu, tampon tamamen boş olacak şekilde tüm işaretçileri başlatır (Şekil 15.6). Bu akış
tamponundan bir karakter okunursa, underflow() işlevi çağrılır. Sonraki karakterleri okumak
için her zaman bu akış tamponu tarafından kullanılan bu işlev, okunan karakterleri kontrol ederek
başlar.
15.13 Dere Tampon Sınıfları 843

giriş tamponu. Karakterler mevcutsa, memcpy() işlevi kullanılarak geri koyma alanına taşınırlar.
Bunlar, en fazla, giriş tamponunun son dört karakteridir. Daha sonra POSIX'in düşük seviyeli G/Ç
fonksiyonu read() standart giriş kanalından bir sonraki karakteri okumak için kullanılır. Tampon
yeni duruma göre ayarlandıktan sonra, okunan ilk karakter döndürülür.

eback()gptr()egptr()

Şekil 15.6. Başlatma Sonrası Get Arabelleği

Örneğin, 'H', 'a', 'l', 'l', 'o' ve 'w' karakterleri read() işlevine yapılan ilk çağrı ile
okunursa, giriş arabelleğinin durumu Şekil 15.7'de gösterildiği gibi değişir. Geri koyma alanı
boştur çünkü tampon ilk kez doldurulmuştur ve henüz geri koyulabilecek karakter yoktur.

eback()gptr() egptr()

H a l l o w

Şekil 15.7. H a l l o w Okuduktan Sonra Tampon Al

Bu karakterler çıkarıldıktan sonra, son dört karakter geri koyma alanına taşınır ve yeni karakterler
okunur. Örneğin, 'e', 'e', 'n' ve '\n' karakterleri bir sonraki read() çağrısı ile okunursa,
sonuç Şekil 15.8'de gösterildiği gibi olur.

eback() gptr() egptr()

l l o w e e n \n

Şekil 15.8. Dört Karakter Daha Okuduktan Sonra Arabellek Al


844 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

İşte bu akış tamponunun kullanımına bir örnek:


// io/inbuf1.cpp
#include <iostream>
#include "inbuf1.hpp"

int main()
{
inbuf ib; // özel akış tamponu oluştur
std::istream in(&ib); // giriş akışını bu tampon ile başlat

char c;
for (int i=1; i<=20; i++) {
// sonraki karakteri oku (tampondan)
in.get(c);

// o karakteri yazdır (ve sifonu çek)


std::cout << c << std::flush;

// sekiz karakterden sonra, iki karakteri akışa geri koy


if (i == 8) {
in.unget();
in.unget();
}
}
std::cout << std::endl;
}

Program bir döngü içinde karakterleri okur ve dışarı yazar. Sekizinci karakter okunduktan sonra,
iki karakter geri konur. Böylece, yedinci ve sekizinci karakterler iki kez yazdırılır.

15.14 Performans Sorunları


Bu bölümde performansa odaklanan konular ele alınmaktadır. Genel olarak, akış sınıfları oldukça
verimli olmalıdır, ancak G/Ç'nin performans açısından kritik olduğu uygulamalarda performans
daha da geliştirilebilir.
Bölüm 15.2.3, sayfa 752'de zaten bir performans sorunundan bahsedilmişti: Yalnızca kodunuzu
derlemek için gerekli olan başlıkları eklemelisiniz. Özellikle, şunları eklemekten kaçınmalısınız
<iostream> eğer standart akış nesneleri kullanılmıyorsa.
15.14 Performans 845
Sorunları

15.14.1 C'nin Standart Akışları ile Senkronizasyon


Varsayılan olarak, sekiz C++ standart akışı - dört dar karakter akışı cin, cout, cerr ve clog
ve bunların geniş karakterli karşılıkları - C standart kütüphanesindeki ilgili dosyalarla senkronize
edilir: stdin, stdout ve stderr. Varsayılan olarak, clog ve wclog sırasıyla cerr ve wcerr
ile aynı akış tamponunu kullanır. Bu nedenle, C standart kütüphanesinde doğrudan bir karşılığı
olmamasına rağmen, varsayılan olarak stderr ile de senkronize edilirler.
Uygulamaya bağlı olarak, bu senkronizasyon genellikle gereksiz bazı ek yükler anlamına
gelebilir. Örneğin, standart C++ akışlarının standart C dosyaları kullanılarak uygulanması, ilgili
akış tamponlarında tamponlamayı engeller. Ancak, akış tamponlarındaki tampon, özellikle
biçimlendirilmiş okuma sırasında bazı optimizasyonlar için gereklidir (bkz. Bölüm 15.14.2, sayfa
845). Daha iyi bir uygulamaya geçişe izin vermek için, sync_with_stdio() statik üye işlevi
ios_base sınıfı için tanımlanmıştır (Tablo 15.47).

Statik Fonksiyon Anlamı


sync_with_stdio() Standart akış nesnelerinin standart C akışlarıyla senkronize
edilip edilmediğini ve eşzamanlılığın desteklenip
sync_with_stdio(false) desteklenmediğini döndürür C++ ve C akışlarının
senkronizasyonunu devre dışı bırakır
(herhangi bir G/Ç işleminden önce çağrılmalıdır)

Tablo 15.47. Standart C++ ve Standart C Akışlarını Senkronize Etme

sync_with_stdio(), standart C akışlarıyla senkronizasyonun açılıp açılmayacağını


belirleyen isteğe bağlı bir Boolean değeri argüman olarak alır. Dolayısıyla, senkronizasyonu
kapatmak için argüman olarak false değerini geçmeniz gerekir:
std::ios::sync_with_stdio(false); // senkronizasyonu devre dışı bırak
Diğer G/Ç işlemlerinden önce senkronizasyonu devre dışı bırakmanız gerektiğini unutmayın.
Herhangi bir G/Ç gerçekleştikten sonra bu işlevin çağrılması, uygulama tanımlı davranışa neden
olur.
Fonksiyon, fonksiyonun çağrıldığı önceki değeri döndürür. Daha önce çağrılmamışsa, standart
akışların varsayılan kurulumunu yansıtmak için her zaman true değerini döndürür.
C++11'den bu yana, standart C akışlarıyla senkronizasyonun devre dışı bırakılmasının, standart
akış nesnesini birden fazla iş parçacığı tarafından kullanmanıza olanak tanıyan eşzamanlılık
desteğini de devre dışı bıraktığını unutmayın, ancak serpiştirilmiş karakterler mümkündür (bkz.
Bölüm 4.5, sayfa 56).

15.14.2 Akarsu Tamponlarında Tamponlama


G/Ç'nin tamponlanması verimlilik açısından önemlidir. Bunun bir nedeni, sistem çağrılarının genel
olarak nispeten pahalı olması ve mümkünse bunlardan kaçınmak için para ödenmesidir. Bununla
birlikte, C++'da en azından girdi için akış tamponlarında tamponlama yapmak için daha ince bir
neden daha vardır: Biçimlendirilmiş G/Ç fonksiyonları akışlara erişmek için akış tampon
yineleyicileri kullanır ve akış tampon yineleyicileri üzerinde çalışmak işaretçiler üzerinde
çalışmaktan daha yavaştır. Fark o kadar büyük değildir, ancak geliştirilmiş uygulamayı haklı
çıkarmak için yeterlidir.
846 Bölüm 15: Akış Sınıflarını Kullanarak
Giriş/Çıkış

sayısal değerlerin biçimlendirilmiş olarak okunması gibi sık kullanılan işlemler için geliştirmeler.
Ancak, bu tür iyileştirmeler için akış tamponlarının tamponlu olması şarttır.
Bu nedenle, tüm G/Ç işlemleri tamponlama için bir mekanizma uygulayan akış tamponları
kullanılarak yapılır. Bununla birlikte, yalnızca bu tamponlamaya güvenmek yeterli değildir, çünkü
üç husus etkili tamponlama ile çelişir:
1. Akış tamponlarını tamponlama olmadan uygulamak genellikle daha basittir. İlgili akışlar sık
kullanılmıyorsa veya yalnızca çıktı için kullanılıyorsa, tamponlama muhtemelen o kadar
önemli değildir. (Çıkış için, akış tampon yineleyicileri ve işaretçiler arasındaki fark giriş için
olduğu kadar kötü değildir; asıl sorun akış tampon yineleyicilerini karşılaştırmaktır). Ancak,
yoğun olarak kullanılan akış tamponları için tamponlama kesinlikle uygulanmalıdır.
2. Unitbuf bayrağı, çıktı akışlarının her çıktı işleminden sonra akışı yıkamasına neden olur.
Buna paralel olarak, flush ve endl manipülatörleri de akışı temizler. En iyi performans için
muhtemelen üçünden de kaçınılmalıdır. Bununla birlikte, örneğin konsola yazarken, tam
satırları yazdıktan sonra akışı temizlemek muhtemelen hala mantıklıdır. Unitbuf, flush veya
endl'i yoğun olarak kullanan bir programa takılıp kaldıysanız, akış tamponunu temizlemek
için sync() kullanmayan ancak uygun olduğunda çağrılan başka bir işlev kullanan özel bir
akış tamponu kullanmayı düşünebilirsiniz.
3. Akışların tie() fonksiyonuyla bağlanması (bkz. Bölüm 15.12.1, sayfa 819) ayrıca akışların
daha fazla yıkanmasına neden olur. Bu nedenle, akışlar yalnızca gerçekten gerekliyse
bağlanmalıdır.
Yeni akış tamponlarını uygularken, bunları önce tamponlama olmadan uygulamak makul olabilir.
Ardından, akış tamponunun bir darboğaz olduğu tespit edilirse, uygulamanın geri kalanında
herhangi bir şeyi etkilemeden tamponlamayı uygulamak hala mümkündür.

15.14.3 Akış Tamponlarını Doğrudan Kullanma


basic_istream ve basic_ostream sınıfının karakter okuyan veya yazan tüm üye
fonksiyonları aynı şemaya göre çalışır: Önce ilgili bir sentry nesnesi oluşturulur ve ardından
işlem gerçekleştirilir. Sentry nesnesinin oluşturulması, potansiyel olarak bağlı nesnelerin
temizlenmesine, girdi için boşlukların atlanmasına ve çok iş parçacıklı ortamlarda kilitleme gibi
uygulamaya özel işlemlere neden olur (bkz. Bölüm 15.5.4, sayfa 772).
Biçimlendirilmemiş G/Ç için, işlemlerin çoğu normalde zaten işe yaramaz. Akışlar çok iş
parçacıklı ortamlarda kullanılıyorsa yalnızca kilitleme işlemi yararlı olabilir. Bu nedenle,
biçimlendirilmemiş G/Ç yaparken, akış tamponlarını doğrudan kullanmak daha iyi olabilir.
Bu davranışı desteklemek için << ve >> operatörlerini akış tamponları ile aşağıdaki gibi
kullanabilirsiniz:
- Bir akış tamponuna bir işaretçiyi operator <<'ye geçirerek, aygıtının tüm girdisinin çıktısını
alabilirsiniz. Bu muhtemelen C++ I/O akışlarını kullanarak dosya kopyalamanın en hızlı
yoludur. Örneğin:
// io/copy1.cpp
#include <iostream>
15.14 Performans 847
Sorunları

int main ()
{
// tüm standart girdiyi standart çıktıya kopyala
std::cout << std::cin.rdbuf();
}

Burada rdbuf(), cin tamponunu verir (bkz. Bölüm 15.12.2, sayfa 820). Böylece, program
tüm standart girdiyi standart çıktıya kopyalar.
- Bir akış tamponuna bir işaretçiyi >> operatörüne geçirerek, doğrudan bir akış tamponuna
okuyabilirsiniz. Örneğin, tüm standart girdiyi standart çıktıya aşağıdaki şekilde de
kopyalayabilirsiniz:
// io/copy2.cpp
#include <iostream>

int main ()
{
// tüm standart girdiyi standart çıktıya kopyala
std::cin >> std::noskipws >> std::cout.rdbuf();
}

skipws bayrağını temizlemeniz gerektiğini unutmayın. Aksi takdirde, girdinin başındaki boşluk
atlanır (bkz. Bölüm 15.7.7, sayfa 789).
Biçimlendirilmiş G/Ç için bile, akış tamponlarını doğrudan kullanmak makul olabilir. Örneğin, bir
döngüde çok sayıda sayısal değer okunuyorsa, döngünün yürütüldüğü tüm süre boyunca var olan
tek bir sentry nesnesi oluşturmak yeterlidir. Daha sonra, döngü içinde boşluklar manuel olarak
atlanır - ws manipülatörünü kullanmak da bir sentry nesnesi oluşturacaktır - ve ardından
num_get faseti (bkz. Bölüm 16.4.1, sayfa 873) sayısal değerleri doğrudan okumak için kullanılır.
Bir akış tamponunun kendi hata durumu olmadığını unutmayın. Ayrıca, kendisine
bağlanabilecek giriş veya çıkış akışı hakkında hiçbir bilgisi yoktur. Bu nedenle, çağrı
// in içeriğini out'a kopyala <<
in.rdbuf();
in hata durumunu bir hata veya dosya sonu nedeniyle değiştiremez.
Bu sayfa kasıtlı olarak boş bırakılmıştır

You might also like