C++ Ogólna charakterystyka obsługi wejścia-wyjścia
Bufor - jest blokiem pamięci stanowiącym pośrednie, tymczasowe miejsce przechowywania przy przesyte informacje z urządzenia do programu lub z programu do urządzenia
-(urządzenia takie jak napędy dyskowe przesyłają zawyczaj dane w blokach po 512 lub więcej bajtów, podczas gdy programy często operują na pojedynczych bajtach ) => bufor umożliwia pogodzenie tych dwóch różnych prędkości przesyłu i przetwarzania informacji.
*np. Załóżmy na przykład, że zadaniem programu jest zliczenie znaków $ znajdujących się w pliku znapisanym na dysku twardym. Taki program mógłby odczytywać z pliku jeden znak, przetwarzać go, odczytywać następny znak itd. Odczyt z pliku pojedynczych znaków wymaga wysokiej aktywności sprzętu i jest powolny. Podejście buforowane podlega na odczycie z dysku dużej porcji danych, umieszczeniu tej porcji w buforze, a następnej odczycie pojedynczych znaków już z buforów. Ponieważ odczyt pojedynczych znaków wymaga wysokiej aktywności sprzętu i jest powolny. Podejście buforowane polega na odczycie z dysku dużej porcji danych , umieszczenie tej porcji w buforze, a następnej odczycie pojedynczych znaków już w z buforów. Ponieważ ocdczyt pojedynczych znaków już z buforów. Ponieważ odczyt pojedynczych bajtów z pamięci jest znacznie szybszy niż z dysku twardego, takie rozwiązanie jest zdecydowanie wydajniejsze, a także mniej obciążające dla sprzętu. Po osiągnięciu końca bufora program powinien oczywiście odczyt z dysku następną porcję danych.
-wejście z klawiatury dostarcza pojedyncze znaki, a więc w tym przypadku program nie potrzebuje buforu pozwalającego dopasować do siebie różne prędkość przesyłu. Buforowanie wejścia z klawiatury umożliwia jednak użytkownikowi skopiowanie i poprawienie danych wejściowych przed przesłaniem ich do programu. W przypadku programu w języku C++ bufor wejściowy jest normalnie opróżniony po naciśnięcia klawisza Enter.
-Przy wyświetlaniu danych wyjściowych program C++ opróżnia bufor wyjsciowy po przesłaniu znaku nowego wiersza
-W zależności od sytuacji program moze opróżnić bufory także w innych sytuacjach, na przykład w przypadku oczekującej operacji wejścia. Oznacza to, że po napotkaniu instrukcji wejścia program opróżnia bufor wyjściowej ze wszystkich danych wyjściowych. Takie właśnie działanie powinny zapewnić implementacje języka C++ zgodnie ze standardami ANSI.
Dzięki zastosowaniu definicji typedef specjalizacje szablonów dla typu char intują klasyczne nieszablonowe implementacje klas I/O. Oto niektóre z tych klas:
- klasa streambuf zapewnia obszar pamięci na bufor wraz z metodami służącymi do wypewniania bufora, odwoływania się do jego zawartości, opróżnia go oraz zarządzania jego pamięcią.
-klasa ios_base reprezentuje ogólne właściwości strumienia informujące m.in. o tym, czy jest on otwarty do odczytu oraz czy jest strumieniem tekstowym czy też binarnym.
-klasa ios jest pochodną klasy ios_base i zawiera wskaźnik składowy na obiekt klasy streambuf
-klasa ostream wywodzi sie z klasy ios i udostępnia metody do obsługi wyjścia
-klasa istream wywodzi sie z klasy ios i udostępnia metody do obsługi wejścia
-klasa iostream jest pochodną klas istream oraz ostream, a więc dziedziczy zarówno metody do obsługi wejścia jak i wyjścia
Dołaczenie do programu pliku iostream powoduje na przykład automatycznie utworzenie ośmiu obiektów strumieni (czterech dla strumieni znaków 8-bitowych i czterech dla strumieni znaków 16-bitowych)
-obiekt cin odpowiada standardowemu strumieniowi wejściowemu. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wejściowym, którym zazwyczaj jest klawiatura. Obiekt wcin jest podobny, z tym że współpracuje z typem wchar_t
-obiekt cout odpowiada standardowemu strumieniowi wyjściowemu. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor. Obiekt wcout jest podobny, z tym że współpracujez typem wchar_t
-obiekt cerr odpowiada standardowemu strumieniowi błędów, który można wykorzystać do wyświetlania komunikatów błędów. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor i nie jest buforowany. Oznacza to, że dane są bezpośrednio na ekran, bez czekania na wypełnienie bufora czy też na pojawienie się znaku nowego wiersza. Obiekt cerr jest podobny, z tym że współpracuje z typem wchar_t
-obiekt clog także odpowiada standardowemu strumieniowi błędów. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor i nie jest buforowany. Obiekt clog jest podobny, z tym, że współpracuje z typem wchar_t
-
Co oznacza stwierdzenie, że obiekt reprezentuje stumień?
Gdy obiekt cout zostaje zdeklarowany w programie przez plik iostream, dysponuje danymi składowymi przechowującymi informacje dotyczące wyjścia, takim jak szerokość pól, które mają być użyte przy wyświetlaniu danych. Liczba wyświetlanych miejsc po przecinku, podstawa systemu liczbowego, która zostanie użyta do wyświetlenia wartości całkowitych, a także adres ok=biektu klasy streambuf opisującego bufor służący do obsługi strumienia wyjściowego. Instrukcja taka jak:
cout << "Wiwat Bjarne";
umieszcza znaki z łańcucha "Wiwat Bjarne" w buforze zarządzonym przez obiekt cout za pośrdenictwa wskazywanego obiektu klasy streambuf. Klasa ostream definiuje użytą w tej instrukcji funkcję operator << (), a także zapewnia obsługę danych składowych obiektu cout, udostępniając wiele różnych metod. Ponadto język C++ pilnuje by dane z bufora były kierowane na wyjście stnadardowe, czyli zazwyczaj monitor, zapewniane przez system operacyjny. Mówiąc w skrócie, jeden koniec strumienia jest połączony z programem, drugi koniec jest połączony z wyjściem standardowym a obiekt cout za pomocą obiektu typu streambuf zarządza przepływem bajtów przez ten strumień.
Operator <<
a) operator buforowy przesunięcia w lewo
-np. x << 3 - połączenie przesunięcia wszystkich bitów binarnej reprezentacji zmiennej x o trzy pozycje w lewo
b) operator wstawiania : -> przeciążony operator
- klasa ostream przedefinicje jednak poprzez przeciążenie operator <<, tak by realizował operację wyjścia dla klasy oftream
-operator wstawiania jest przeciążony żeby rozpoznawać wszystkie podsrtawowe typy C++:
*unsigned char
*signed char
*char
*short
*unsigned short
*int
*unsigned int
*long
*unsigned long
*long long (C++11)
*unsigned long long (C++11)
*float
*double
*long double
-klasa ostream zapewnia definicję funckji operator << () dla każdego z tych typów danych
cout < value;
zmienna value będzie jednego z wyżej wymienionych typów, program w języku C++ będzie mógł odnaleźć dla niej funkcji operatorową o odpowiedniej sygnaturze. Np. cout << 88 odpowiada następującemu pototypowi metody:
odtream & operator << (int)
Taki prototyp sygnalizuje, iż funkcja operator << () pobiera jeden argument typu int. To właśnie ten składnik powyższej deklaracji jest zgodny z wartością 88. Prototyp wskazuje również, że funkcja zwraca referencję do obiektu klasy ostream.
Ta własność umożliwia sklejanie danych wyjściowych.
-w klasie ostream zdefiniowano funkcja operatora wstawiania dla następujących typów wskaźnikowych:
*const signed char*
*const unsigned char *
*const char*
*void *
- w języku C++ łańcuch jest reprezentowany przez wskaźnik na miejsc położenia tego łańcucha w pamięci. Wskaźnik ten może przyjąć postać nazwy tablicy typu char, jawnego wskaźnika na typ char lub łańcucha ujętego w cudzysłów. Zatem wszystkie poniższe instrukcje cout wyświetla łańcuchy:
char name [20] = "Jurek Ogórek";
char * pn = "Kiełbasa i sznurek";
cout << "Hej!";
cout << name;
cout << pn;
Metody te określają, kiedy przerwać wyświetlanie znaków, na podstawie położenia w łańcuchu kończącego znaku pustego (NULL+).
- w języku C++ wskaźnik na dowolny inny typ jest traktowany jak wskaźnik typu void* i wyświetlana jest liczbowa reprezentacje adresu. jeśli chcemy wyświetlić adres łańcucha, musimy wykonać na nim rzutowanie na inny typ, tak jak pokazano to w w nastepującym fragmencie kodu.
int eggs = 12;
char * amount = "tuzin";
cout << amount;
cout << (void*) amount //wyświetlane są adresy zmiennych
-sklejanie danych wyjściowych
*wszystkie wcielenia operatora wstawiania zgodnie ze swoją definicją zwracają typ ostream &. Oznacza, że wszystkie prototypy są postaci:
ostream & operator << (typ);
Symbol typ oznacza danych typ, który ma być wyświetlany. Typ zwrócony ostream & sugeruje, że użycie tego typu zwraca referencję do obiektu użytego do wywołania operatora. Innymi słowy, wartość zwracana przez tę funkcję operatorową jest tym samym obiektem, który wywołuje operator. Na przykład onstrukcja cout << "poczęstunek" zwraca obiekt cout. Dzięki tej własnosci za pomocą operatora wstawiania można sklejać dane wejściowe. Rozwoimy
* jako przykład następującą instrukcji:
cout << "Mamy" << cout << "niewklute kurczaki\n";
Wyrażenie ocut < "Mamy" wyświetla łańcuch i zwraca obiekt cout, a tym samym redukuje powyższa instrukcje do postaci:
cout << count << "niewyklute kurczai \n";
i tak dalej.
Inne metody klasy ostream
-> put ()
-pierwotne metoda put() miała nastepujący prototyp:
ostream & put (char);
-obecny standard jest równoważny, z tym, że metodę tę zdefiniowano w postaci szablonu, żeby umożliwić obsługę typu wchar_t.
-metodę tę wywołuje się, stosując zwyczajną rotację wywoływania metody klasy:
cout.put('W'); // wyświetl znak W
*cout - obiekt wywołujący
*metoda put() - funkcja składowa klasy - funkcja ta zswraca referencję do obiektu wywołującego, dzięki czemu mamy możliwość sklejania danych wejściowych np.:
cout.put('T').put('O'); //Wyświetlenie słowa to oprzy czynu dwóch wywołań metod put()
-dysponując właściwym prototypem, możemy funkcję put() wywoływać z argumentami typów liczbowych różnych od char (np.int) i pozwolić mechanizmowi prototypowania funkcji automatyczne przekierować argument na odpowiednią wartość typu char. Możemy np. użyć nasptępującego kodu:
cout.put(65);//wyświetla znak A
cout.put(66.3); //wyświeetla znak B
Takie zachowanie jest użyteczne w przypadku wersji języku C++ wczśniejszych niż 2.0 - w tych wersjach stała znakowa były reprezentowane przez wartość typu int .
cout << 'W' // w starych wersjach wyświetla 87
-w przypadku niektórych kompilatorów funkcja put() była błędzie przeciążenie dla takich typów argumentów:
*char
*unsigned char
*signed char
W takiej sytuacji wywołania funkcji put() a argumentem typu int jest niejednoznaczne, ponieważ wartość typu int może zostać przkonwertowana na każdy z tych trzech typów.
-> write()
-powoduje wyświetlenie całego łancucha, który ma być wyświetlony, a drugi parametr określa, ile znaków należy wyświetlić. Wywołanie funkcji write() dla obiektu cout powoduje wywołanie specjalizacji dla typu char, a więc typem zwracanym będzie ostream &
funkcjonowanie metody write
#include<iostrweam>
#include<ctring>
int mian()
{
using std::cout;
using std::endl;
const char* state1 = "Floryda";
const char* state2 = "Kansas";
const char* state3 = "Eufona";
int len = std::strlen(state2);
cout << "Inkrementacja indeksu pętli:\n";
int i;
for(i=1; i<=len;i++)
{
cout.write(state2, i); //wypisuje i znaków łańcucha state2
cout<<endl;
}
//sklej dane wejściowe
cout << "Dekrementacja indeksów pętli:\n";
for(i=len; i>0; i--)
cout.write(state2, i)<<endl;
//przekroczenie długości łańcucha
cout << "Przekroczenie długości łańcucha: \n";
cout.write(state2, len+5) <<endl;
return0;
}
Niektóre kompilatory mogą sygnalizować, że tablica state1 oraz state3 zostały w programie zdefiniowane, lecz nie są używane. Jest to całkiem prawidłowe, ponieważ te dwie tablice są tam po to, aby zapewnić istnienie dawnych przed i za tablicą state2. Dzięki temu można zobaczy, co się stanie w przypadku niewłaściwego odwołania do łańcucha state2.
Inkrementacja indeksu pętli:
K
Ka
Kan
Kans
Kansa
Kansas
Dekrementacja indeksu pętli:
Kansas
Kansa
Kans
Kan
Ka
K
Przekroczenie długości łańcucha
Kansas Eufo
-Zauważmy, że wywołanie funkcji cout.write() zwraca obiekt cout. Dzieje się tak ponieważ metoda write() zwraca referencje do obiektu, który ją wywołał, a więc w tym przypadku referencję do obiektu cout.
-Dzięki takiemu rozwiązaniu możliwe jest sklejanie danych wyjściowych, ponieważ wywoływanie funkcji cout.write() zostaje zastąpione jej wartością zwracaną, czyli obiektem cout:
cout.write(state2, i)<<endl;
-Należy ponmadto zuważyć, że metoda write() nie przestaje wyświetlać znaków automatycznie po napotkaniu znaku pustego. Wyświetla ona tyle znaków, ile jej się zleci, nawet jeśli oznacza to wykroczenie poza granice danego łańcuch "Kansas" umieszczono pomiędzy dwoma innymi, tak aby sąsiednie obszary pamięci, oraz sposobem ułożenia danych w pamięci. Łańcuch "Kansas" np zajmuje 6 bajtów, lecz ten konkretny kompilator wydaje się wyrównywac łańcuchy do wielkość 4 bajtów, a więc łańcuch "Kansas". A więc ze wzg. na różnice pomiędzy kompilatorami ostani wiersz danych wyjściowych programu może być inny.
-Metody write() mozna używać także w stosunku do danych liczbowych. W takim przypadku należy podać jej adres danej liczby zrzutowanych na typ char*:
long val = 560031841
cout.write((char*) & val, sizeof(long));
Oczywiście liczb nie zostaną w ten sposób przekonwertowania na odpowiednie znaki zamiast tego zostanie wysłana bitowa reprezentacja w postaci, jakiej jest ona przechowywana w pamięci. Np. 4-bajtowa wartość typu long równa 560031841 zostanie przesłana jako 4 odzielne bajty. Na urzadzeniu wyjściowym, tj. monitor, każdy z tych bajtów zostanie zinterprentowany tak, jakby był kodem ASCII (lub jakimś innym), czyli liczba ta będzie kombinacją 4 znaków, najprawdopodbniej bez sensu.
-(urządzenia takie jak napędy dyskowe przesyłają zawyczaj dane w blokach po 512 lub więcej bajtów, podczas gdy programy często operują na pojedynczych bajtach ) => bufor umożliwia pogodzenie tych dwóch różnych prędkości przesyłu i przetwarzania informacji.
*np. Załóżmy na przykład, że zadaniem programu jest zliczenie znaków $ znajdujących się w pliku znapisanym na dysku twardym. Taki program mógłby odczytywać z pliku jeden znak, przetwarzać go, odczytywać następny znak itd. Odczyt z pliku pojedynczych znaków wymaga wysokiej aktywności sprzętu i jest powolny. Podejście buforowane podlega na odczycie z dysku dużej porcji danych, umieszczeniu tej porcji w buforze, a następnej odczycie pojedynczych znaków już z buforów. Ponieważ odczyt pojedynczych znaków wymaga wysokiej aktywności sprzętu i jest powolny. Podejście buforowane polega na odczycie z dysku dużej porcji danych , umieszczenie tej porcji w buforze, a następnej odczycie pojedynczych znaków już w z buforów. Ponieważ ocdczyt pojedynczych znaków już z buforów. Ponieważ odczyt pojedynczych bajtów z pamięci jest znacznie szybszy niż z dysku twardego, takie rozwiązanie jest zdecydowanie wydajniejsze, a także mniej obciążające dla sprzętu. Po osiągnięciu końca bufora program powinien oczywiście odczyt z dysku następną porcję danych.
-wejście z klawiatury dostarcza pojedyncze znaki, a więc w tym przypadku program nie potrzebuje buforu pozwalającego dopasować do siebie różne prędkość przesyłu. Buforowanie wejścia z klawiatury umożliwia jednak użytkownikowi skopiowanie i poprawienie danych wejściowych przed przesłaniem ich do programu. W przypadku programu w języku C++ bufor wejściowy jest normalnie opróżniony po naciśnięcia klawisza Enter.
-Przy wyświetlaniu danych wyjściowych program C++ opróżnia bufor wyjsciowy po przesłaniu znaku nowego wiersza
-W zależności od sytuacji program moze opróżnić bufory także w innych sytuacjach, na przykład w przypadku oczekującej operacji wejścia. Oznacza to, że po napotkaniu instrukcji wejścia program opróżnia bufor wyjściowej ze wszystkich danych wyjściowych. Takie właśnie działanie powinny zapewnić implementacje języka C++ zgodnie ze standardami ANSI.
Dzięki zastosowaniu definicji typedef specjalizacje szablonów dla typu char intują klasyczne nieszablonowe implementacje klas I/O. Oto niektóre z tych klas:
- klasa streambuf zapewnia obszar pamięci na bufor wraz z metodami służącymi do wypewniania bufora, odwoływania się do jego zawartości, opróżnia go oraz zarządzania jego pamięcią.
-klasa ios_base reprezentuje ogólne właściwości strumienia informujące m.in. o tym, czy jest on otwarty do odczytu oraz czy jest strumieniem tekstowym czy też binarnym.
-klasa ios jest pochodną klasy ios_base i zawiera wskaźnik składowy na obiekt klasy streambuf
-klasa ostream wywodzi sie z klasy ios i udostępnia metody do obsługi wyjścia
-klasa istream wywodzi sie z klasy ios i udostępnia metody do obsługi wejścia
-klasa iostream jest pochodną klas istream oraz ostream, a więc dziedziczy zarówno metody do obsługi wejścia jak i wyjścia
Dołaczenie do programu pliku iostream powoduje na przykład automatycznie utworzenie ośmiu obiektów strumieni (czterech dla strumieni znaków 8-bitowych i czterech dla strumieni znaków 16-bitowych)
-obiekt cin odpowiada standardowemu strumieniowi wejściowemu. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wejściowym, którym zazwyczaj jest klawiatura. Obiekt wcin jest podobny, z tym że współpracuje z typem wchar_t
-obiekt cout odpowiada standardowemu strumieniowi wyjściowemu. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor. Obiekt wcout jest podobny, z tym że współpracujez typem wchar_t
-obiekt cerr odpowiada standardowemu strumieniowi błędów, który można wykorzystać do wyświetlania komunikatów błędów. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor i nie jest buforowany. Oznacza to, że dane są bezpośrednio na ekran, bez czekania na wypełnienie bufora czy też na pojawienie się znaku nowego wiersza. Obiekt cerr jest podobny, z tym że współpracuje z typem wchar_t
-obiekt clog także odpowiada standardowemu strumieniowi błędów. Domyślnie strumień ten jest skojarzony ze standardowym urządzeniem wyjściowym, którym zazwyczaj jest monitor i nie jest buforowany. Obiekt clog jest podobny, z tym, że współpracuje z typem wchar_t
-
Co oznacza stwierdzenie, że obiekt reprezentuje stumień?
Gdy obiekt cout zostaje zdeklarowany w programie przez plik iostream, dysponuje danymi składowymi przechowującymi informacje dotyczące wyjścia, takim jak szerokość pól, które mają być użyte przy wyświetlaniu danych. Liczba wyświetlanych miejsc po przecinku, podstawa systemu liczbowego, która zostanie użyta do wyświetlenia wartości całkowitych, a także adres ok=biektu klasy streambuf opisującego bufor służący do obsługi strumienia wyjściowego. Instrukcja taka jak:
cout << "Wiwat Bjarne";
umieszcza znaki z łańcucha "Wiwat Bjarne" w buforze zarządzonym przez obiekt cout za pośrdenictwa wskazywanego obiektu klasy streambuf. Klasa ostream definiuje użytą w tej instrukcji funkcję operator << (), a także zapewnia obsługę danych składowych obiektu cout, udostępniając wiele różnych metod. Ponadto język C++ pilnuje by dane z bufora były kierowane na wyjście stnadardowe, czyli zazwyczaj monitor, zapewniane przez system operacyjny. Mówiąc w skrócie, jeden koniec strumienia jest połączony z programem, drugi koniec jest połączony z wyjściem standardowym a obiekt cout za pomocą obiektu typu streambuf zarządza przepływem bajtów przez ten strumień.
Operator <<
a) operator buforowy przesunięcia w lewo
-np. x << 3 - połączenie przesunięcia wszystkich bitów binarnej reprezentacji zmiennej x o trzy pozycje w lewo
b) operator wstawiania : -> przeciążony operator
- klasa ostream przedefinicje jednak poprzez przeciążenie operator <<, tak by realizował operację wyjścia dla klasy oftream
-operator wstawiania jest przeciążony żeby rozpoznawać wszystkie podsrtawowe typy C++:
*unsigned char
*signed char
*char
*short
*unsigned short
*int
*unsigned int
*long
*unsigned long
*long long (C++11)
*unsigned long long (C++11)
*float
*double
*long double
-klasa ostream zapewnia definicję funckji operator << () dla każdego z tych typów danych
cout < value;
zmienna value będzie jednego z wyżej wymienionych typów, program w języku C++ będzie mógł odnaleźć dla niej funkcji operatorową o odpowiedniej sygnaturze. Np. cout << 88 odpowiada następującemu pototypowi metody:
odtream & operator << (int)
Taki prototyp sygnalizuje, iż funkcja operator << () pobiera jeden argument typu int. To właśnie ten składnik powyższej deklaracji jest zgodny z wartością 88. Prototyp wskazuje również, że funkcja zwraca referencję do obiektu klasy ostream.
Ta własność umożliwia sklejanie danych wyjściowych.
-w klasie ostream zdefiniowano funkcja operatora wstawiania dla następujących typów wskaźnikowych:
*const signed char*
*const unsigned char *
*const char*
*void *
- w języku C++ łańcuch jest reprezentowany przez wskaźnik na miejsc położenia tego łańcucha w pamięci. Wskaźnik ten może przyjąć postać nazwy tablicy typu char, jawnego wskaźnika na typ char lub łańcucha ujętego w cudzysłów. Zatem wszystkie poniższe instrukcje cout wyświetla łańcuchy:
char name [20] = "Jurek Ogórek";
char * pn = "Kiełbasa i sznurek";
cout << "Hej!";
cout << name;
cout << pn;
Metody te określają, kiedy przerwać wyświetlanie znaków, na podstawie położenia w łańcuchu kończącego znaku pustego (NULL+).
- w języku C++ wskaźnik na dowolny inny typ jest traktowany jak wskaźnik typu void* i wyświetlana jest liczbowa reprezentacje adresu. jeśli chcemy wyświetlić adres łańcucha, musimy wykonać na nim rzutowanie na inny typ, tak jak pokazano to w w nastepującym fragmencie kodu.
int eggs = 12;
char * amount = "tuzin";
cout << amount;
cout << (void*) amount //wyświetlane są adresy zmiennych
-sklejanie danych wyjściowych
*wszystkie wcielenia operatora wstawiania zgodnie ze swoją definicją zwracają typ ostream &. Oznacza, że wszystkie prototypy są postaci:
ostream & operator << (typ);
Symbol typ oznacza danych typ, który ma być wyświetlany. Typ zwrócony ostream & sugeruje, że użycie tego typu zwraca referencję do obiektu użytego do wywołania operatora. Innymi słowy, wartość zwracana przez tę funkcję operatorową jest tym samym obiektem, który wywołuje operator. Na przykład onstrukcja cout << "poczęstunek" zwraca obiekt cout. Dzięki tej własnosci za pomocą operatora wstawiania można sklejać dane wejściowe. Rozwoimy
* jako przykład następującą instrukcji:
cout << "Mamy" << cout << "niewklute kurczaki\n";
Wyrażenie ocut < "Mamy" wyświetla łańcuch i zwraca obiekt cout, a tym samym redukuje powyższa instrukcje do postaci:
cout << count << "niewyklute kurczai \n";
i tak dalej.
Inne metody klasy ostream
-> put ()
-pierwotne metoda put() miała nastepujący prototyp:
ostream & put (char);
-obecny standard jest równoważny, z tym, że metodę tę zdefiniowano w postaci szablonu, żeby umożliwić obsługę typu wchar_t.
-metodę tę wywołuje się, stosując zwyczajną rotację wywoływania metody klasy:
cout.put('W'); // wyświetl znak W
*cout - obiekt wywołujący
*metoda put() - funkcja składowa klasy - funkcja ta zswraca referencję do obiektu wywołującego, dzięki czemu mamy możliwość sklejania danych wejściowych np.:
cout.put('T').put('O'); //Wyświetlenie słowa to oprzy czynu dwóch wywołań metod put()
-dysponując właściwym prototypem, możemy funkcję put() wywoływać z argumentami typów liczbowych różnych od char (np.int) i pozwolić mechanizmowi prototypowania funkcji automatyczne przekierować argument na odpowiednią wartość typu char. Możemy np. użyć nasptępującego kodu:
cout.put(65);//wyświetla znak A
cout.put(66.3); //wyświeetla znak B
Takie zachowanie jest użyteczne w przypadku wersji języku C++ wczśniejszych niż 2.0 - w tych wersjach stała znakowa były reprezentowane przez wartość typu int .
cout << 'W' // w starych wersjach wyświetla 87
-w przypadku niektórych kompilatorów funkcja put() była błędzie przeciążenie dla takich typów argumentów:
*char
*unsigned char
*signed char
W takiej sytuacji wywołania funkcji put() a argumentem typu int jest niejednoznaczne, ponieważ wartość typu int może zostać przkonwertowana na każdy z tych trzech typów.
-> write()
-powoduje wyświetlenie całego łancucha, który ma być wyświetlony, a drugi parametr określa, ile znaków należy wyświetlić. Wywołanie funkcji write() dla obiektu cout powoduje wywołanie specjalizacji dla typu char, a więc typem zwracanym będzie ostream &
funkcjonowanie metody write
#include<iostrweam>
#include<ctring>
int mian()
{
using std::cout;
using std::endl;
const char* state1 = "Floryda";
const char* state2 = "Kansas";
const char* state3 = "Eufona";
int len = std::strlen(state2);
cout << "Inkrementacja indeksu pętli:\n";
int i;
for(i=1; i<=len;i++)
{
cout.write(state2, i); //wypisuje i znaków łańcucha state2
cout<<endl;
}
//sklej dane wejściowe
cout << "Dekrementacja indeksów pętli:\n";
for(i=len; i>0; i--)
cout.write(state2, i)<<endl;
//przekroczenie długości łańcucha
cout << "Przekroczenie długości łańcucha: \n";
cout.write(state2, len+5) <<endl;
return0;
}
Niektóre kompilatory mogą sygnalizować, że tablica state1 oraz state3 zostały w programie zdefiniowane, lecz nie są używane. Jest to całkiem prawidłowe, ponieważ te dwie tablice są tam po to, aby zapewnić istnienie dawnych przed i za tablicą state2. Dzięki temu można zobaczy, co się stanie w przypadku niewłaściwego odwołania do łańcucha state2.
Inkrementacja indeksu pętli:
K
Ka
Kan
Kans
Kansa
Kansas
Dekrementacja indeksu pętli:
Kansas
Kansa
Kans
Kan
Ka
K
Przekroczenie długości łańcucha
Kansas Eufo
-Zauważmy, że wywołanie funkcji cout.write() zwraca obiekt cout. Dzieje się tak ponieważ metoda write() zwraca referencje do obiektu, który ją wywołał, a więc w tym przypadku referencję do obiektu cout.
-Dzięki takiemu rozwiązaniu możliwe jest sklejanie danych wyjściowych, ponieważ wywoływanie funkcji cout.write() zostaje zastąpione jej wartością zwracaną, czyli obiektem cout:
cout.write(state2, i)<<endl;
-Należy ponmadto zuważyć, że metoda write() nie przestaje wyświetlać znaków automatycznie po napotkaniu znaku pustego. Wyświetla ona tyle znaków, ile jej się zleci, nawet jeśli oznacza to wykroczenie poza granice danego łańcuch "Kansas" umieszczono pomiędzy dwoma innymi, tak aby sąsiednie obszary pamięci, oraz sposobem ułożenia danych w pamięci. Łańcuch "Kansas" np zajmuje 6 bajtów, lecz ten konkretny kompilator wydaje się wyrównywac łańcuchy do wielkość 4 bajtów, a więc łańcuch "Kansas". A więc ze wzg. na różnice pomiędzy kompilatorami ostani wiersz danych wyjściowych programu może być inny.
-Metody write() mozna używać także w stosunku do danych liczbowych. W takim przypadku należy podać jej adres danej liczby zrzutowanych na typ char*:
long val = 560031841
cout.write((char*) & val, sizeof(long));
Oczywiście liczb nie zostaną w ten sposób przekonwertowania na odpowiednie znaki zamiast tego zostanie wysłana bitowa reprezentacja w postaci, jakiej jest ona przechowywana w pamięci. Np. 4-bajtowa wartość typu long równa 560031841 zostanie przesłana jako 4 odzielne bajty. Na urzadzeniu wyjściowym, tj. monitor, każdy z tych bajtów zostanie zinterprentowany tak, jakby był kodem ASCII (lub jakimś innym), czyli liczba ta będzie kombinacją 4 znaków, najprawdopodbniej bez sensu.
Komentarze
Prześlij komentarz