Funkcja operatorowa >>

0
#include <iostream>
#include <string.h>

using namespace std;

class CRekord{
    char* m_pNazwa;
    const int m_nrID = licznik;
    static int licznik;
    short int m_pKod[20];

public:
    CRekord(char* naz);
    ~CRekord();
    CRekord(const CRekord& wzorzec);
    CRekord operator= (const CRekord& prawy){
        if(this != (&prawy)){
            this->~CRekord();
            m_pNazwa = prawy.m_pNazwa;
            for(int i=0; i<20 ; i++){
                m_pKod[i] = prawy.m_pKod[i];
            }
        }
    }
    void ustaw();
    friend istream& operator>>(istream &we, CRekord &prawa);
    void wypisz();
};

int CRekord::licznik = 0;

CRekord::CRekord(char* naz){
    licznik++;
    m_pNazwa = new char[50];
    strncpy(m_pNazwa,naz,50);
    for(int i=0; i<20 ; i++){
        m_pKod[i]=0;
    }
}

CRekord::~CRekord(){
    delete [] m_pNazwa;
}

CRekord::CRekord(const CRekord& wzorzec){
    licznik++;
    m_pNazwa = wzorzec.m_pNazwa;
    for(int i=0; i<20 ; i++){
        m_pKod[i] = wzorzec.m_pKod[i];
    }

}


void CRekord::wypisz(){
    cout<<m_pNazwa<<endl<<m_nrID<<endl<<licznik<<endl<<m_pKod[0]<<endl;
}

void CRekord::ustaw(){
    short int temp[20];
    cout<<"Podaj stary kod"<<endl;
        for (int i=0; i<20 ; i++) {
            cin>>temp[i];
            if(m_pKod[i]!=temp[i]) return;
        }

    cout<<"Podaj nowy kod"<<endl;
        for(int i=0; i<20 ; i++){
            cin>>m_pKod[i];
        }
}

istream& operator>>(istream &we, CRekord &prawa){
    if (&cin == &we){
        cout<<"Podaj nazwe"<<endl;
        char temp[50];
        cin>>temp;
        strncpy(prawa.m_pNazwa,temp,50);

        cout<<"Podaj kod"<<endl;
        for (int i=0; i<20 ; i++){
            cin>>prawa.m_pKod[i];
        }
    }
    return we;
}

int main(){
    CRekord a("napis");
    a.wypisz();

    CRekord b(a);
    b.wypisz();

    cin>>b;




    return 0;
}

Może mi ktoś wytłumaczyć te warunek?

 friend istream& operator>>(istream& we, CRekord& wzor)
{
if (&cin == &we)
{

Co to jest &cin? Adres strumenia czy coś :D?

1

cin to obiekt typu std::istream, więc tak jak każdy normalny obiekt ma jakiś adres. Sprawdzasz tutaj czy wczytasz z cin czy może jakiegoś innego strumiena.
http://en.cppreference.com/w/cpp/io/cin

2
  1. Smrodek od niepoprawnie używanej inkrementacji: http://4programmers.net/Forum/1101404
  2. if(&cin==&we) cout<<"Podaj nazwe"<<endl; - reszta niech leci jak jest,
  3. Tu
char temp[50];
    ...
    strncpy(prawa.m_pNazwa,temp,50);

Mazanie po pamięci, musisz zrobić jak w konstruktorze lub użyć strdup
4. Powinno być: CRekord::CRekord(const char *naz)
5.

char *strdup(const char *str)
  {
   if(!str) return str;
   size_t size=strlen(str)+1;
   char *ret=new char[size];
   memcpy(ret,str,size);
   return ret;
  }

w konstruktorze też lepiej to użyj.
6. tu CRekord::CRekord(const CRekord& wzorzec) nazwę również musisz skopiować.

5

@kmph ma rację, to jest chore. &cin to adres obiektu strumienia standardowego wejścia. To porównanie ma na celu zapewnienie, że wczytujesz wyłącznie ze standardowego wejścia, ignorując wszystko inne.

Ponadto:

  1. W kodzie masz UB (operator kopiowania i konstruktor kopiujący kopiują wskaźnik, a potem jest double delete)
  2. Poczytaj o regule zero: https://rmf.io/cxx11/rule-of-zero/
  3. Używaj standardowych kontenerów zamiast je ręcznie implementować. std::string nie gryzie. std::vector też nie.
  4. Nie powinieneś wypisywać nic w metodzie wczytującej ze strumienia.

Jeśli to nie jest Twój kod, a tylko się na nim wzorujesz - nie wzoruj się, jest zły.

1
if(this != (&prawy)){
            this->~CRekord();

To już chyba błąd sam w sobie. https://isocpp.org/wiki/faq/dtors Powtarzają tam setki razy, by nigdy nie wywoływać destruktora, chyba że się używa placement new

0
// Interaktywność pomijam. Jeśli będzie potrzebna, to niech się nią zajmie
// jakiś zewnętrzny wrapper albo funkcja main(). IMHO tak będzie najlepiej

#include <istream>
#include <ostream>
#include <string>
#include <vector>
#include <cstddef>
#include <utility>

class CRekord
{
  std::string nazwa;
  int ID;
  static std::size_t licznik;
  static constexpr std::vector<int>::size_type kod_rozmiar = 20;
  static constexpr short kod_domyslny = 0;
  std::vector<short> kod;

public:
  CRekord(std::string nazwa) :
    nazwa(nazwa), ID(licznik++),kod(kod_rozmiar, kod_domyslny) {}
  CRekord(const CRekord &cr) : nazwa(cr.nazwa), ID(licznik++), kod(cr.kod) {}
  CRekord(CRekord &&cr) noexcept :
    nazwa(std::move(cr.nazwa)), ID(std::move(cr.ID)), kod(std::move(cr.kod)) {}
  CRekord &operator = (const CRekord &cr)
    {nazwa = cr.nazwa; kod = cr.kod; return *this;}
  CRekord &operator = (CRekord &&cr) noexcept
  {
    nazwa = std::move(cr.nazwa);
    kod = std::move(cr.kod);
    ID = std::move(cr.ID);
    return *this;
  }
  friend std::istream &operator >> (std::istream &we, CRekord &cr)
  {
    std::string pom_nazwa; std::vector<short> pom_kod(kod_rozmiar);
    we >> pom_nazwa;
    for(std::vector<short>::size_type i = 0; i < kod_rozmiar; ++i)
      we >> pom_kod[i]; // Zadowolony z preinkrementacji, @_13th_Dragon? ;P
    if(we.good())
    {
      cr.nazwa = std::move(pom_nazwa);
      cr.kod = std::move(pom_kod);
    }
    return we;
  }
  friend std::ostream &operator << (std::ostream &wy, CRekord const &cr)
  {
    wy << cr.nazwa << std::endl << cr.ID << std::endl << cr.kod[0] << std::endl;
    return wy; // Pomijam wypisywanie licznika, bo to IMHO bez sensu
  }
  bool ustaw
    (std::vector<short> const &stary_kod, std::vector<short> const &nowy_kod)
  {
    if(kod == stary_kod)
    {
      kod = nowy_kod;
      return true;
    }
    else return false;
  } // Pobraniem starego_kodu i nowego_kodu niech się zajmie wrapper
};

std::size_t CRekord::licznik = 0;

Komentarze?

1

Kolega xavi robi ten kod w ramach nauki na poprawkę z egzaminu z c++.

W załączniku macie polecenia do egzaminu. (z lat ubiegłych, chomiki rlz) Nie można stosować wektorów etc, a jedynie to o co prosi prowadzący w poleceniu.

PS. Udzielam się tu czysto informacyjnie, bo poznałem zadania, które ktoś wrzucił na naszą grupę na fb, a ja egzamin mam za sobą.

2

EDIT!! Przepraszam, pomyliłem się, wprowadziłem do kodu poprawki.

EDIT2. if(&cin == &we) chyba jednak zbędne. W zadaniu jest o tym, że operator ma zostać skojarzony ze strumieniem wejścia istream, a więc niekoniecznie cin - to jest strumień standardowego wejścia, a to różnica.

Dobrze zatem. Też miałem nauczycieli z takimi wymogami, rozumiem.

Do przygotowania się do egzaminu:

#include <iostream>
#include <istream>
#include <cstring>
#include <iomanip>

class CRekord
{
  // Skoro wykładowca żąda… Nie widzę zalet takiego rozwiązania nad std::string
  char* m_pNazwa;
  const int m_nrID;
  static int licznik;
  short int m_pKod[20];

public:
  // Nie mogę definiować w klasie żadnych metod, bo inaczej będą inline.
  // Wszystkie definicje muszą wylądować poza klasą.
  // Jak pisze @kq, przy takiej deklaracji nie możesz prosto tworzyć obiektów tej klasy za pomocą literału znakowego.
  CRekord (char* naz);
  ~CRekord();
  CRekord(const CRekord& prawy);
  // Operator przypiasania musi zwracać CRekord&, bo inaczej nie będzie można
  // przypisywać kaskadowo. Powinni byli gdzieś Ci powiedzieć, dlaczego tak.
  CRekord& operator = (const CRekord& prawy);
  // Może zwracanie bool będzie lepsze. True jeśli przypisanie się udało,
  // false w.p.p.
  bool ustaw_kod();
  friend std::istream& operator >> (std::istream& we, CRekord& prawy);
};

int CRekord::licznik = 0;

// jednocześnie przypisuję i inkrementuję licznik
// Mam nadzieję rozumiesz czym się różni licznik++ od ++licznik?
CRekord::CRekord(char* naz) : m_nrID(licznik++)
{
  // new char[51] bo miejsce na null terminator
  m_pNazwa = new char[51];
  std::strncpy(m_pNazwa, naz, 50);
  // Dodanie null terminatora; jeśli naz (razem z null terminatorem) jest 
  // dłuższa niż 50 znaków, to strncpy nie doda null terminatora automatycznie
  m_pNazwa[50] = '\0';
  // sizeof(m_pKod) działa, gdyż m_pKod jest tablicą alokowaną statycznie
  // Gdyby to był tylko wskaźnik do tablicy, (czyli np. tablica alokowana
  // dynamicznie bądź tablica przekazana jako argument funkcji), to już tak nie
  // można by było.
  // Aha, i taka inicjalizacja też zadziała tylko dla tablic liczbowych
  // i tylko dla zera.
  memset(m_pNazwa, 0, sizeof(m_pKod));
}

CRekord::~CRekord()
{
  delete[] m_pNazwa;
}

// Ten design jest nieodporny na tworzenie się kopii tymczasowych,
// ale trudno – skoro wykładowca żąda. Chodzi mi o to, że przy takim projekcie,
// w niektórych sytuacjach (np. gdy jakaś funkcja zwraca obiekt klasy CRekord
// albo gdy obiekty tej klasy są w std::vector i swapujesz vector) może się
// nieoczekiwanie inkrementować licznik i zmieniać wartość m_nrID. Poczytaj
// o różnicy między copy constructor a move costructor, jeśli chcesz wiedzieć
// więcej
CRekord::CRekord(const CRekord& prawy) : m_nrID(licznik++)
{
  // Teraz 51 żeby się na pewno null terminator skopiował
  // Można tak bo wiadomo że prawy.m_pNazwa ma null terminator i rozmiar
  // nie większy niż 51 znaków z nullem włącznie
  std::strncpy(m_pNazwa, prawy.m_pNazwa, 51);
  // Chyba lepiej niż for?
  // Uwaga do kopiowania tablic liczb całkowitych memcpy się nadaje, ale do
  // kopiowania tablic bardziej skomplikowanych obiektów
  // jak np. CRekord już niekoniecznie
  std::memcpy(m_pKod, prawy.m_pKod, sizeof(m_pKod));
}

// Tak się definiuje operator poza klasą
CRekord& CRekord::operator = (const CRekord& prawy)
{
  if(this != &prawy)
  {
    // Swoim porządkiem to używanie tych wszystkich memcpy memset i innych
    // to jest straszne świństwo. Naprawdę, może jednak fory są lepsze. Nie
    // widzę zalet takich tablic nad std::vector
    std::strncpy(m_pNazwa, prawy.m_pNazwa, 51);
    std::memcpy(m_pKod, prawy.m_pKod, 20*sizeof(short int));
  }
  return *this;
}

bool CRekord::ustaw_kod()
{
  short int stary_kod[20];
  for(int i = 0; i < 20; ++i)
    std::cin >> stary_kod[i];
  short int nowy_kod[20];
  for(int i = 0; i < 20; ++i)
    std::cin >> stary_kod[i];
  if(std::memcmp(m_pKod, stary_kod, sizeof(m_pKod)))
  {
    std::memcpy(m_pKod, nowy_kod, sizeof(m_pKod));
    return true;
  }
  else return false;
}

std::istream& operator >> (std::istream& we, CRekord& prawy)
{
  // setw chroni przed przepełnieniem bufora, jak zadanie każe
  // Uwaga! Jest duża szansa, że komitet standaryzacyjny C++ zarządzi, że
  // operator >> ma przestać działać z tablicami znaków!!
  // http://cplusplus.github.io/LWG/lwg-active.html#2499
  // Kolejny powód by nie używać tablic znaków
  we >> std::setw(51) >> prawy.m_pNazwa;
  for(int i = 0; i < 20; ++i)
      we >> prawy.m_pKod[i];
  return we;
}

Czekam na gromy od pozostałych forowiczów ;P

Ostrzegam, że nie przetestowałem żadnego z tych moich dwóch kodów.

1

Btw, czym się różni cin od obiektu
std::istream kek(cin.rdbuf());?
No niczym chyba, oba obiekty korzystają z tego samego bufora i zachowują się tak samo. A ten kod nie pozwoli mi wczytać do kek. W ogóle jakieś herezje straszne w tym temacie. A ten kod jest jeszcze gorszy.

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