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.