Zadanie z przeciążaniem operatora strumieniowego

Odpowiedz Nowy wątek
2018-07-04 21:12
0

Witam,

Napotkałem problem podczas rozwiązywania zadań przed kolokwium z Programowania Obiektowego w C++ .
Na początek wstawię poniżej nagłówek klasy, której dotyczy zadanie.

#ifndef STRINGLIST_H
#define STRINGLIST_H
#include <list>
#include <string>
#include <iostream>
#include <utility>

class StringList
{
private:
    std::list<std::string> l;
    std::list<std::string>::iterator current_line; // moje pomysły, raczej zły tok myslenia
    int current_word = 0; // to rowniez

public:
    StringList operator+(const std::string &s);
    StringList operator-(const std::string &s);

    std::list<std::string> getList();

    friend std::ostream& operator<<(std::ostream &os, const StringList &sl);
    friend std::ostream& operator<<(std::ostream &os, const StringList &sl);

};

std::ostream& operator<<(std::ostream &os, const StringList &sl);
std::ostream& operator<<(std::ostream &os, const StringList &sl);

#endif // STRINGLIST_H

No i tresc zadania

Zadanie 4.
Zaprogramuj dla klasy StringList wyjściowy operator strumieniowy, którego kolejne wywołania tego operatora będą skutkować przepisaniem na strumień jednego, kolejnego wyrazu z napisu. Jeżeli osiągnięty zostanie koniec pierwszego należy przejść do kolejnego itd. W przypadku dojścia do końca listy działanie jest nieokreślone.

No i mam problem jak podawać na strumien po jednym wyrazie? Ok, gdyby trzeba było podać wszystkie na raz nie ma problemu, robie sobie 2 pętle i wszystko gładko działa. Ale w jaki sposób zapamiętywać w ktorym miejscu skonczyłem (tzn który wyraz powinien byc rzucony gdy wywołamy kolejny raz operator << ) ?
Podjąłem jakieś próby ze zmiennymi pomocniczymi : iterator wskazujacy na linie i int wskazujacy na numer słowa które powinno byc wyswietlone w dalej linii, ale wydaje mi się że nie tędy droga.
Będę wdzięczny za wszelkie wskazówki.

Pozostało 580 znaków

2018-07-04 21:14
0

może naprowadzeniem na to jak podejsc do problemu bedzie kolejne zadanie?

Zadanie 5.
Zmodyfikuj operator z poprzedniego zadania tak, aby w przypadku dojścia do końca listy rzucany był wyjątek std::out_of_range, ale następne wywołanie tego operatora powodowało rozpoczęcie odczytywania od początku.

Pozostało 580 znaków

2018-07-04 22:34

naklepałem coś na szybko, przeanalizuj i wyciągnij wnioski

struct test
{
    std::istringstream input{ "To jest tekst." };
    friend std::ostream& operator<<(std::ostream &os,  test &sl);
};

std::ostream &operator<<(std::ostream &os,  test& a) {
    std::string data;
    a.input >> data;
    os<<data;
    return os;
 }
// w int main()
 test moj_test;
   std::cout << moj_test << "\n";
   std::cout << moj_test << "\n";

edit:
a co do samej listy to iteratory.


We are the 4p. Existence, as you know it, is over. We will add your biological and technological distinctiveness to our own. Resistance is futile
edytowany 2x, ostatnio: revcorey, 2018-07-04 22:44

Pozostało 580 znaków

2018-07-04 22:51
0

Tylko jeszcze problem z konstruktorem kopiującym jak mam stringstream jako pole składowe klasy... Kompilator generuje mi błąd jak konstruktora kopiujacego nie napiszę ręcznie, a nie wiem jak napisac, aby kopiował także stan obiektu typu stringstream. Konstruktor kopiujący tutaj sie wykonuje

StringList StringList::operator+(const std::string &s){
    for(const auto &v : l)
        if(v == s)
            return *this;

    StringList result(*this); //tu pewnie dziala konstruktor kopiujacy
    result.l.push_back(s);

    return result;
}
a sprawdzałeś chociaż dokumentację tego obiektu czy od razu stwierdziłeś nie wiem? podpowiem ci tellg i seekg. - revcorey 2018-07-04 22:54

Pozostało 580 znaków

2018-07-05 00:20
0

Tylko jeszcze problem z konstruktorem kopiującym jak mam stringstream jako pole składowe klasy...

Bo to jest strumień taki sam jak iostream. Trzymaj w klasie referencję do niego.


Nie pisz na priv. Zadaj dobre pytanie na forum.
dałbyś radę w trochę prymitywny sposób tzn. zainicjować nowego istringstream napisem z starego a następnie za pomocą seekg ustawić się odpowiednio. - revcorey 2018-07-05 08:14

Pozostało 580 znaków

2018-07-05 09:55
0
StringList::StringList(const StringList &other)
    : stream(other.stream.str())
{
    this->l = other.l;
    this->stream.seekg(other.stream.tellg());
    this->stream.seekp(other.stream.tellp());
    this->stream.setstate(other.stream.rdstate());
}

/home/piotrek/kolokwiumOOP/powtorzenie/stringlist.cpp:10: error: passing ‘const stringstream {aka const std::__cxx11::basic_stringstream<char>}’ as ‘this’ argument discards qualifiers [-fpermissive]
this->stream.seekg(other.stream.tellg());
^

Wie ktoś może dlaczego to generuje błędy?

" passing ‘const stringstream" - revcorey 2018-07-05 09:59
ale pole w klasie nie jest const, jest to normalny obiekt std::stringstream - piotreekk 2018-07-05 10:00
pokaż więcej kodu a zawsze możesz zrobić this->stream.str(other.str()); I miej na uwadze też to co YooSy napisał wyżej. - revcorey 2018-07-05 10:06
Po namyśle zamiast kopiowania napisu rozważ ss2 << ss1.rdbuf(); gdzie ss2 to stream do którego kopiujesz dane z ss1. - revcorey 2018-07-05 10:09
rzuciłem niżej swój kod. Tą drogą nie dojdę do rozwiazania? Wiesz moze co jest przyczyną tego błędu kompilatora? Chciałbym wiedzieć na przyszłość już - piotreekk 2018-07-05 10:10

Pozostało 580 znaków

2018-07-05 10:09
0

#ifndef STRINGLIST_H
#define STRINGLIST_H
#include <list>
#include <string>
#include <iostream>
#include <sstream>

class StringList
{
private:
    std::list<std::string> l;
    std::stringstream stream;

public:
    StringList(const StringList &other);
    StringList operator+(const std::string &s);
    StringList operator-(const std::string &s);

    std::list<std::string> getList();

    friend std::ostream& operator<<(std::ostream &os, const StringList &sl);
    friend std::istream& operator>>(std::istream &is, const StringList &sl);

};

std::ostream& operator<<(std::ostream &os, const StringList &sl);
std::istream& operator>>(std::istream &is, const StringList &sl);

#endif // STRINGLIST_H
#include "stringlist.h"
#include <algorithm>

StringList::StringList(const StringList &other)
    : stream(other.stream.str())
{
    this->l = other.l;
    this->stream.seekg(other.stream.tellg());
    this->stream.seekp(other.stream.tellp());
    this->stream.setstate(other.stream.rdstate());
}

StringList StringList::operator+(const std::string &s){
    for(const auto &v : l)
        if(v == s)
            return *this;

    StringList result(*this); //tu pewnie dziala konstruktor kopiujacy
    result.l.push_back(s);
    stream << s;

    return result;
}

StringList StringList::operator-(const std::string &s){
    StringList result(*this);

    auto pos = std::find(l.begin(), l.end(), s);
    if(pos != l.end())
        result.l.erase(pos);

    return result;
}

std::list<std::string> StringList::getList(){
    return l;
}

std::ostream& operator<<(std::ostream &os, const StringList &sl){
    std::string word;
   // sl.stream >> word blad
    os << word;
    return os;
}
edytowany 1x, ostatnio: piotreekk, 2018-07-05 10:13
tak jak ci napisalem wyżej passing const. Nie możesz wywołać tellg na constowym obiekcie. W dokumentacji masz napisane jak tellg działa. - revcorey 2018-07-05 10:16
to było 7 lat temu. Jak w obecne gcc wrzuciłem i usunąłem const gra. - revcorey 2018-07-05 10:34

Pozostało 580 znaków

2018-07-05 10:13
1

Jakbym dostał do "code review" kod, który pełnia wymagania tego zadania, to bym porządnie skrytykował autora.
Takie zachowanie StringList jest bardzo dziwne i nieoczekiwane i późniejsze czytanie kodu, który korzysta z takiego operatora jest bardzo nieczytelne trudne do zrozumienia i będzie prowadzić do błędów.
Są pewne standardy kodowania, których przestrzeganie pomaga czytać i rozumieć kod.

Taką funkcjonalność realizowałbym za pomocą klasy pomocniczej którą bym nazwał PrintNextOne lub coś w ten deseń.

Kod który spełnia to durne wymaganie:

// mutable std::list<std::string>::const_iterator current_line;

std::ostream& operator<<(std::ostream &os, const StringList &sl) {
     if (!os.good()) {
          return os;
     }
     if (sl.current_line == sl.l.end()) {
          os.setstate(std::ios::failbit);
          return os;
     }
     os << *sl.current_line;
     ++sl.current_line;
     return os;
}

Przy czym każda modyfikacja StringList musi ponownie ustawić wartość current_line by zapobiec UB.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 2x, ostatnio: MarekR22, 2018-07-05 11:03
o właśnie o to mi chodziło, tylko nie wiedziałem jak to zaimplementować. Dziękuję bardzo - piotreekk 2018-07-05 10:16
a co do pozniejszego czytania kodu i jego rozumienia masz rację, ale w ramach nauki wydaje mi się, że zadanie może wiele nauczyc :) Edit. Chociaz chwila, to nie działa tak jak trzeba. A co jesli w jednej linii pojawi się wiecej niż jedno słowo? Chcemy zwrócic tylko 1 za jednym wywołaniem operatora, pozniej kolejne słowo jesli jest w tej linii, jesli nie to przejsc do kolejnej linii - piotreekk 2018-07-05 10:17
poza tym jak mogę inkrementować iterator sl, skoro jest ono const - piotreekk 2018-07-05 10:24
on wskazuje na wartość stałą i możesz iterować nim. - revcorey 2018-07-05 10:45
już widzę, ale mogę iterować nim poprzez std::next , bo ++ nie działa - piotreekk 2018-07-05 10:49
dodaj słowo kluczowe mutable. - MarekR22 2018-07-05 11:04

Pozostało 580 znaków

2018-07-05 10:55
0

a czy mogę dać jako zmienna prywatna w klasie w jakiś sposób zwykłego inta, który będzie wskazywał na numer słowa w aktualnej linii i go inkrementować w jakiś sposób mimo że mam stałą referencję w funkcji przeciazania operatora na const StringList &sl ?

Pozostało 580 znaków

2018-07-05 11:55
0

A czy nie moznabyłoby tego zrobic w ten sposob?

std::ostream& operator<<(std::ostream &os, const StringList &sl){
    if(sl.wc_in_line == 0) sl.countWords();

    if(sl.current_word == sl.wc_in_line){
        ++sl.current_line;
        sl.current_word = 0;
        sl.wc_in_line = 0;
        sl.countWords();
    }

    os << "Current Line: " << *sl.current_line << " wc in line: " << sl.wc_in_line << " current word nr: " << sl.current_word << std::endl;

    std::istringstream iss(*sl.current_line);
    int word_nr = 0;
    std::string word;

    while(iss >> word){
        if(word_nr == sl.current_word){
            os << word;
            break;
        }
        ++word_nr;
    }

    ++sl.current_word;
    return os;
void StringList::countWords() const{
    std::istringstream iss(*current_line);
    std::string word;
    while(iss >> word)
        ++wc_in_line;
}

StringList StringList::operator+(const std::string &s){
    for(const auto &v : l)
        if(v == s)
            return *this;

    StringList result(*this); //tu pewnie dziala konstruktor kopiujacy
    result.l.push_back(s);
    current_line = l.begin();
    current_word = 0;
    wc_in_line = 0;

    return result;
}
class StringList
{
private:
    std::list<std::string> l;
    mutable std::list<std::string>::const_iterator current_line;
    mutable int current_word;
    mutable int wc_in_line;
    void countWords() const;
} .....

Tylko nie wiem dlaczego nie przechodzi mi do kolejnej linii

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0