• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ Pętle i wprowadznie danych tekstowych (wprowadzanie tekstu aż do natknięcia się na znak końcowy)

Object Storage Arubacloud
+1 głos
1,106 wizyt
pytanie zadane 25 stycznia 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
edycja 25 stycznia 2018 przez Jakub 0

Hej, ucząc się c++ natknąłem się na temat którego naprawdę mam już solidnie dość crying Chodzi o wprowadzanie danych z klawiatury, pliku dopóki nie zostanie wykryte EOF (koniec) itd... W książce jest taki przykład:

char ch;
	int count = 0;

		while (cin.get(ch)) {
			++count;
		}
		cout << endl << count << endl;

		cin.clear();
		cin.get();

Ogólnie go rozumiem, cin.get(ch) konwertowane jest na wartości typu bool czyli true lub false (1 lub 0) tworząc warunek pętli, kombinacja <CTRL>+<Z>+<ENTER> wszystko przerywa...

Jednak kiedy zapisze:

while (cin.get(ch)==true)

Mam błąd kompilacji , jak to możliwe skoro to wyrażenie i tak zwraca bool'a a ja tylko to sprawdzam? Natomiast to:

while (!cin.get(ch))

To zadziała (oczywiście odwrotna sytuacja, pętla nie wykona się ani razu kiedy nie natkniemy się na koniec). Przecież to też sprawdzanie czy wyrażenie zwraca true czy false... Już po kilka razy czytam te strony i coraz bardziej mnie to irytuje...

Dziękuje za pomoc i pozdrawiam :)

1 odpowiedź

+3 głosów
odpowiedź 25 stycznia 2018 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 25 stycznia 2018 przez Sebastian Fojcik
 
Najlepsza

Musisz się przyzwyczaić, że w trakcie nauki nie zawsze będziesz rozumiał "dlaczego tak? dlaczego to działa?". Zastanawiałeś się skąd się biorą << przy cout? (to nie jest część języka C++ ;-)

Ale nie o tym. Zastanawiasz się nad faktem dlaczego działa:

while (cin.get(ch))
while (!cin.get(ch))

Ale nie działa

while (cin.get(ch) == true )

Aby zrozumieć to w 100% musiałbyś znać obiektowość i wiedzieć czym jest funkcja konwertująca. Bo cin, to właśnie obiekt klasy istream. No ale łopatologicznie spróbuję. 
Dlaczego cin.get(   ) w ogóle może stać wewnątrz warunku pętli? 
Spójrz na dokumentację klasy istreamhttp://www.cplusplus.com/reference/istream/istream/

Tutaj są napisane wszystkie funkcje, z których korzysta cin. W gąszczu zapewne niezrozumiałych dla Ciebie (jeszcze) nazw i liter znajdują się 2 szczególnie Cię interesujące w tym momencie. W sekcji Public member functions inherited from ios. Mianowicie:

operator!
operator bool     (C++11)

Te funkcje opisują jak cin będzie się zachowywał, jeśli użyjesz go w wyrażeniu warunkowym (if, while itp.).

No to wejdźmy do opisu funkcji operator boolhttp://www.cplusplus.com/reference/ios/ios/operator_bool/

Co widzimy? Zrobię screeny: Tutaj jestem przełączony na standard C++98

Co my tu mamy? Widzimy, że funkcja operator bool jest zdefiniowana w tym przypadku jako operator void*() const. Tym const się nie przejmuj. Funkcja ta według deklaracji zwraca void*. Czyli wskaźnik typu void. (poczytaj o nich, bo nie jest to treścią tego pytania). Niżej widzimy opis zwracanej wartości. Po przetłumaczeniu mamy mniej więcej coś takiego: Jeśli jest ustawiona jakaś flaga błędu, np. EOF (koniec pliku, wczytywania), to zwraca null pointer. Czyli wskaźnik na NULL. Czyli po konwersji na liczbę będzie to (interpretowane wewnątrz warunku pętli, ifa jako fałsz).

ALE: czytamy dalej, w przeciwnym razie (gdy nie ma flagi błędu) zwracana jest jakaś inna wartość. Czyli może to być 5, ale może to być też -22135324. No i porównanie takiej liczby z wartością true w C++ nie ma sensu, bo przy takim porównaniu:

int n = 5;
if( n == true )
	cout << "prawda" << endl;

To będzie fałsz i instrukcja cout się nie wykona. Mój kompilator (Visual) wyrzuca mi nawet ostrzeżenie, bo porównuję int z bool. Dlaczego nie działa? Bo porównujesz int z bool, więc kompilator zamieni bool na int i ze stałej true zrobi wartość 1. Więc w rzeczywistości porównujesz 5 == 1. Dlatego nie działa. Natomiast takie coś już zadziała:

int n = 5;
if( n )
	cout << "prawda" << endl;
if( !n )
	cout << "falsz" << endl;

Widzisz podobieństwo do Twojego problemu teraz?

Użycie operatora wykrzyknik (!) w kontekście cin jest opisane w dokumentacji funkcji operator!. Ale pozwól, że daruję już sobie omawianie jej. Jest ciekawsza rzecz. Problem wystąpił u Ciebie, bo zapewne nie piszesz w standardzie C++11. (dziwię się, bo mamy 2018 rok, a kompilatory nadal domyślnie nie mają ustawionej zgodności z C++11). Teraz ten sam screenshot z dokumentacji funkcji operator bool tylko jestem przełączony na zakładki C++11.

Widzimy inne zapisy. Po pierwsze, deklaracja teraz wygląda tak: explicit operator bool() const. Teraz dopiero jest to prawdziwa funkcja operator bool, bo (jeśli nie umiesz obiektowości i funkcji konwertujących, to uwierz mi na słowo), funkcja zwraca wartość bool. No ale kiedy ta funkcja jest wywoływana? Ano... dzieje się to niejawnie, za Twoimi plecami, gdy stawiasz cin do warunku pętli, if jako wyrażenie warunkowe.

Przeczytajmy opis zwracanej wartości. Po przetłumaczeniu: funkcja zwraca (rozum jako: cin przyjmuje wartość) false, gdy ustawiona jest flaga failbit lub badbit oraz true gdy żadna z nich.

Gdyby nie było (pewnie tajemniczego dla Ciebie "jeszcze") słowa explicit, to porównanie:

(cin.get(ch) == true)

Miałoby sens. Niestety dodanie powyższego słówka kluczowego nie zezwala na użycie tego w ten sposób. (bo zaszłaby niejawna konwersja, której nie chcemy). Spowodujemy w takim przypadku błąd kompilacji.

(uwaga, trochę zaawansowane)
Zauważ jednak, że napotkanie końca pliku spowoduje ustawienie flagi eofbit, ale jeśli ta flaga jest ustawiona, to nasz cin wcale nie przyjmie wartości false (przyjmie tylko dla failbit i badbit). Jednakże przy takim wczytywaniu, które Ty realizujesz, czyli w warunku pętli, po przesłaniu znaku Ctrl+Z funkcja ustawi dodatkowo flagę failbit. Pojawi się failbit, cin przyjmie wartość false i pętla się zakończy. Czyli jednocześnie zepsujesz strumień w pewnym sensie i będziesz musiał czyścić flagi błędów, aby dalej móc się nim posługiwać :-)

Podsumowując. Jeśli jest jakiś problem, to zawsze należy sięgać do dokumentacji. Niestety jeśli uczysz się dopiero, to musisz wykazać się pokorą, że nie wszystko do tej pory jesteś fizycznie w stanie zrozumieć. Wiele rzeczy przyjmij jako dogmat, idź dalej z nauką. Ucz się ogólnie o języku, i wchodź w szczegóły stopniami. Dowiedz się czym jest obiektowość (tak ogólnie), a potem ucz się o konwersjach, które tutaj opisałem.

Jeśli chcesz, to mogę Ci podać przykład programiku, gdzie używam tego opretator bool() i jak to działa, ale jeśli masz dość, to zrozumiem :-)

Pytaj dalej jak coś. Chętnie odpowiem :-)

komentarz 25 stycznia 2018 przez Jakub 0 Pasjonat (23,120 p.)
Bardzo ci dziękuje, powiem że byłem już mega zrezygnowany... Sprawdzałem co 5 minut czy jest odpowiedź i nawet pytałem na kilku innych portalach... Ten rozdział z książki mnie mega wymęczył, teraz muszę tylko sobie zrobić notatki pisząc je w zrozumiałym dla siebie stylu i języku ;) Ogólnie u mnie historia jest taka że kiedyś coś tam znałem z OOP (z kursu P.MZ), tam były jednak zupełne podstawy i sporo pozapominałem. Teraz ucząc się z książki by poznać bardziej solidnie c++ uzmysławiam sobie ile rzeczy jeszcze nie znam i nie rozumiem (nawet na poziomie pętli czy if'ów). Ostatnio mnie przeraża złożoność cpp i ogólnie technologi. Tyle tego jest że człowiek nie wie za co się zabrać... W każdym bądź razie bardzo dziękuje za złożoną odpowiedź, widać tu wielkie starania by mi pomóc :) . Jak coś mi jeszcze przyjdzie do głowy na ten temat to się zapytam, serdecznie pozdrawiam ;)
1
komentarz 25 stycznia 2018 przez Sebastian Fojcik Nałogowiec (43,040 p.)

Kurs obiektowości pana Zelenta bardzo pomaga w zrozumieniu czym jest obiektowość i poznać podstawy. Pomija on jednak (z oczywistych względów, tworzenie filmu wymaga więcej pracy niż napisanie książki) wiele, wiele, wiele szczegółów. Czasami istotnych, czasami nie. Zależnie od tego jak ciekawski jest uczeń. Ty jesteś ciekawski, bo stawiasz pytania. To dobrze. Tak należy się uczyć :-)

Jeśli przeraża Cię złożenie technologii i cpp, to może uspokoję w ten sposób:

im więcej wiesz, tym łatwiej pojmujesz nowe rzeczy

Idąc tym tokiem rozumowania, jak poznasz raz w życiu czym jest obiektowość (np. w C++), to nauka innego języka będzie wyglądała tak:

- Hmm chcę napisać aplikację na Androida obiektowo, więc napiszę teraz klasę... w sumie, to nigdy nie programowałem w Javie, ale zerknę do Google jak wygląda zapis klasy... Mhm... nawet podobnie do C++, okej, mogę pisać.

W ten sposób jesteś w stanie pojąć i zrozumieć mnóstwo rzeczy z różnych technologii, bo są one po prostu powtarzalne. (Musisz tylko dojść do tego etapu). 

Powodzenia :-)

komentarz 25 stycznia 2018 przez monika90 Pasjonat (22,940 p.)
edycja 25 stycznia 2018 przez monika90

@Sebastian Fojcik,

Podsumowując, gdybyś pisał w standardzie C++11, to porównanie == true by zadziałało

Nie jest to prawdą. Nie jest też prawdą, że eofbit ma wpływ na to co zwraca explicit operator bool albo operator !.

komentarz 25 stycznia 2018 przez Sebastian Fojcik Nałogowiec (43,040 p.)

Ups, czytając dokumentację funkcji operator bool rzeczywiście pominąłem specyfikator explicit. Gwoli ścisłości wyjaśniam (dla zaawansowanych):

Gdyby nie było explicit, to porównanie miałoby sens. Dlaczego więc explicit to psuje? Przykładowy kod:

#include <iostream>

using namespace std;

class Klasa1
{
public:
    operator bool()
    {
        return true;
    }
};
class Klasa2
{
public:
    explicit operator bool()
    {
        return true;
    }
};

int main()
{
    Klasa1 k1;
    Klasa2 k2;

    if( k1 )
        cout << "k1" << endl;
    if( k2 )
        cout << "k2" << endl;
    if( k1 == true )
        cout << "k1 == true" << endl;
    //if( k2 == true )         BŁĄD!
    //    cout << "k2 == true" << endl;

    return 0;
}

Przy próbie skompilowania tego programu w standardzie C++11, gdybym nie umieścił komentarzy, to porównanie k2 == true wywołałoby błąd kompilacji. Ponieważ przy sprawdzaniu warunku == zaszłaby niejawna konwersja, na którą się nie zgadzamy (użyliśmy explicit). Wyżej też widać, że funkcja konwertująca bez specyfikatora explicit pozwala na owe porównanie.

Co do eofbit tutaj również przyznaję się do swojej nieuwagi. Gdy ta flaga błędu jest ustawiona, to operator bool nadal zwróci true. Aczkolwiek przy takim wczytywaniu w pętli jak robi Jakub, przesłanie znaku Ctrl+Z wywoła jednocześnie eofbit oraz failbit, który to właśnie powoduje, że operator bool zwraca false. Mały szczegół, ale w niektórych przypadkach istotny. Dzięki za zwrócenie uwagi. Zaraz poprawię odpowiedź.

Nie uważam jednak, żeby za tak drobne mankamenty oceniać całą moją odpowiedź za negatywną. Choć patrząc na Twoje statystyki oddawania głosów na tym forum, po prostu tak już masz. Przykre ;-)

komentarz 25 stycznia 2018 przez Jakub 0 Pasjonat (23,120 p.)

Mi za to się znowu temat pogmatwał xD Ale jak poczytam o tym explicit  to może będzie lepiej.

1
komentarz 25 stycznia 2018 przez Sebastian Fojcik Nałogowiec (43,040 p.)

Nie nieeee, mój powyższy komentarz jest bardziej dla potomnych albo osób zaawansowanych, które również dostrzegły błąd w mojej wcześniejszej odpowiedzi. 

Cały ten explicit nie powinien Cię teraz interesować. Jak już wcześniej wspomniałem. Czasami jest tak, że nie możesz czegoś fizycznie zrozumieć, bo żeby wiedzieć jak coś działa, musisz wiedzieć jak coś innego działa i coś innego działa i tak dalej...

Jeżeli Twoja książka zawiera w sobie opis obiektowości (w zaawansowanym stopniu) to nie ma sensu wyprzedzać już teraz. Naucz się solidnie podstaw samego języka C++, a takie zagwozdki dotyczące biblioteki standardowej zostaw sobie na później, jak już będziesz umiał wszystkie odpowiedzi odczytywać samodzielnie z dokumentacji :-)

Podobne pytania

0 głosów
1 odpowiedź 62 wizyt
+1 głos
1 odpowiedź 6,907 wizyt
0 głosów
5 odpowiedzi 509 wizyt
pytanie zadane 26 marca 2016 w C i C++ przez frycek Nowicjusz (120 p.)

92,576 zapytań

141,426 odpowiedzi

319,652 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...