prośba o ocene kodu w krótkim programie z klasami

0

Ponizej krotki trzyplikowy programik korzystajacy z klas, może ktoś wskazać w nim miejsca, które są do poprawy, chodzi mi o styl, rozwiązania, składnie itd., żeby to bardziej profesjonalnie wyglądało, o tym że powinienem użyć vector zamiast tablicy Sprzedawca wiem.

//main.cpp
#include <iostream>
#include "sales.h"

using namespace std;

int main()
{
    Sales Sprzedawca[5];
    char znak;
    string nazwisko;
    double sprzedaz[4];

    for(int i=0;i<5;i++)
    {
        cout<<"wpisz pusty (1), wpisz nazwisko i pusta sprzedaz(2), wpisz nazwisko wypelnij sprzedaz(3)"<<endl;
        cin>>znak;
        switch(znak)
        {
        case '1':
            {
            Sprzedawca[i]=Sales();
            break;
            }
        case '2':
            {
                cout<<"podaj nazwisko"<<endl;
                cin.ignore();
                getline(cin,nazwisko);
                Sprzedawca[i]=Sales(nazwisko);
                break;
            }
        case '3':
            {
                cout<<"podaj nazwisko"<<endl;
                cin.ignore();
                getline(cin,nazwisko);
                for(int i=0;i<4;i++)
                {
                    cout<<"podaj sprzedaz za "<<i+1<<"kwartal"<<endl;
                    cin>>sprzedaz[i];
                }
                Sprzedawca[i]=Sales(nazwisko,sprzedaz);
            }
        }
    }
    for(int i=0;i<5;i++)
        Sprzedawca[i].pokaz();

    return 0;
}

//sales.h
#include <iostream>

using namespace std;

const int quaters=4;

class Sales
{
    string sprzedawca;
    double sales[quaters];
    double averange;
    double maksymal;
    double minimal;

public:
    Sales();
    Sales(string handlarz);
    Sales(string handlarz, double sprzedaz[]);
    void UstawPola(double tablicaSprzedazy[]);
    void pokaz();
};

//sales.cpp
#include <iostream>
#include "sales.h"

using namespace std;

Sales::Sales()
{
    sprzedawca="brak";
    for(int i=0;i<4;i++)
        sales[i]=0;
        maksymal=0;
        minimal=0;
        averange=0;
}
Sales::Sales(string handlarz)
{
    sprzedawca=handlarz;
    for(int i=0;i<4;i++)
        sales[i]=0;
        maksymal=0;
        minimal=0;
        averange=0;
}
Sales::Sales(string handlarz, double sprzedaz[])
{
    sprzedawca=handlarz;
    for (int i=0;i<4;i++)
        sales[i]=sprzedaz[i];
        Sales::UstawPola(sprzedaz);
}
void Sales::UstawPola(double tablicaSprzedazy[])
{
    double maks, mini, srednia;
    maks=tablicaSprzedazy[0];
    mini=tablicaSprzedazy[0];
    srednia=0;
    for(int i=0;i<4;i++)
    {
        if(maks<tablicaSprzedazy[i])
            maks=tablicaSprzedazy[i];
        if(mini>tablicaSprzedazy[i])
            maks=tablicaSprzedazy[i];
        srednia=srednia+tablicaSprzedazy[i];
    }
        averange=srednia/4;
        maksymal=maks;
        minimal=mini;
}
 void Sales::pokaz()
 {
     cout<<"sprzedawca o nazwisku "<<sprzedawca<<" sprzedal:"<<endl;
     cout<<"sprzedaz w Ikw: "<<sales[0]<<endl;
     cout<<"sprzedaz w IIkw: "<<sales[1]<<endl;
     cout<<"sprzedaz w IIIkw: "<<sales[2]<<endl;
     cout<<"sprzedaz w IVkw: "<<sales[3]<<endl;
     cout<<"najlepszy kwartal wynik: "<<maksymal<<endl;
     cout<<"najgorszy kwartal wynik: "<<minimal<<endl;
     cout<<"srednio na kwartal: "<<averange<<endl;
 }



5
  • liczby magiczne
  • kod w stylu C (c-arrays)
  • niekonsekwentne wcięcia kodu
  • wielki main
  • mieszanie polskiego i angielskiego w nazwach symboli. Używaj tylko i wyłącznie angielskiego (to ma być odruch).
  • kasa w double oznacza błędy zaokrągleń i wizytę urzędu skarbowego ;)
2

generalnie to trudno tu znaleźć dłuższy pozytywny fragment, poczynając od zupełnie elementarnych:
a) brakujące includy. Na jakim środowisku to Ci się pozytywnie kompiluje? (w MSVC musiałem dodać lub chyba zmienić kolejność, nie pamiętam)
b)

Sprzedawca[i]=Sales(nazwisko,sprzedaz); 

to nie jest prawidłowe wywołanie konstruktora.

...
aż do niuansów:
x) drukowanie samej siebie bez możliwości zmiany, typowy błąd początkujących. A skąd wiesz, że ktoś będzie chciał wywołać coś podobnego do pokaz() w powiązaniu z cout?
y) żadnej zdrowej koncepcji public/ukryte/enkapsulacja tu nie widać. Drobny przykład: co, jeśli ktoś będzie potrzebował użyć 'średniej' w dalszym algorytmie
z)

const int quaters=4;

Byłoby mu dobrze wewnątrz klasy (o ile w ogóle - @MarekR22 dobrze mówi)

1
MarekR22 napisał(a):
  • niekonsekwentne wcięcia kodu

Wręcz błędne

MarekR22 napisał(a):
  • liczby magiczne

Mimo wprowadzenia jednej stałej, o dziwo

1

o tym że powinienem użyć vector zamiast tablicy Sprzedawca wiem

I nie tylko, wewnątrz klasy Sales , aż prosi się aby zastąpić tablicę sales kontenerem typu vector lub array.

1
srk71 napisał(a):

Ponizej krotki trzyplikowy programik korzystajacy z klas

Podsumowanie: program "z użyciem klas" może być bardzo daleki od programowania obiektowego.

0

Wielkie dzięki za wskazówki dla wszystkich

x) drukowanie samej siebie bez możliwości zmiany, typowy błąd początkujących. A skąd wiesz, że ktoś będzie chciał wywołać coś podobnego do pokaz() w powiązaniu z cout?

Możesz mi to wyjaśnić jak powinienem to inaczej zrobić?

1
srk71 napisał(a):

Wielkie dzięki za wskazówki dla wszystkich

x) drukowanie samej siebie bez możliwości zmiany, typowy błąd początkujących. A skąd wiesz, że ktoś będzie chciał wywołać coś podobnego do pokaz() w powiązaniu z cout?

Możesz mi to wyjaśnić jak powinienem to inaczej zrobić?

Przynajmniej umieszczenie referencji do ostream w argumentach *)

void pokaz(ostream & out){
...

Lub zaimplementowanie operatora << Sales , lub jeszcze inne pomysły, pożądane aby jednocześnie rozwiązywały problem y)

*) wybór akurat tej klasy - ktoś może wybrać inną, ale to możesz stosować na początek.

0

Sprzedawca[i]=Sales(nazwisko,sprzedaz);
to nie jest prawidłowe wywołanie konstruktora.

W ksiązce Prata ten sposób wywołania konstruktora określony jest jako jawny, można też wywołać go niejawnie

Sales Sprzedawca[i](nazwisko,sprzedaz);

ale wtedy program sie nie kompiluje i błąd: expression list treated as compound expression in initializer [-fpermessive]
Przy wywołaniu ```Sprzedawca[i]=Sales(nazwisko,sprzedaz); `` kompiluje się i wyświetla wszystko tak jak trzeba. Możesz mi wytłumaczyć z czego to wynika i jak mam wywołąć konstruktor z drugim argumentem w postaci tablicy (sprzedaz - to tablica) prawidłowo?

Przynajmniej umieszczenie referencji do ostream w argumentach *)

void pokaz(ostream & out){
...

Możesz podać przykładową definicje tej funkcji przesyłającą konkretne pola na ekran.

0
AnyKtokolwiek napisał(a):
Sprzedawca[i]=Sales(nazwisko,sprzedaz); 

to nie jest prawidłowe wywołanie konstruktora.

@AnyKtokolwiek możesz rozwinąć? srk71 konstruuje obiekt tymczasowy i przypisuje go do innego obiektu. Ja nie widzę w tym żadnego problemu.

0

Wcześniej @AnyKtokolwiek napisał, że to jest nieprawidłowe wywoałnie konstruktora, nie rozumiem też dlaczego to działa a to
Sales Sprzedawca[i](nazwisko,sprzedaz); już nie

0
MarekR22 napisał(a):
AnyKtokolwiek napisał(a):
Sprzedawca[i]=Sales(nazwisko,sprzedaz); 

to nie jest prawidłowe wywołanie konstruktora.

@AnyKtokolwiek możesz rozwinąć? srk71 konstruuje obiekt tymczasowy i przypisuje go do innego obiektu. Ja nie widzę w tym żadnego problemu.

OK. lepszy jesteś ode mnie :) Za bardzo się zmigroiwałęm do C# i Javy

Można dyskutować o niuansach, np. zaburzenia od konstruktora kopiującego, gdy ten ma nietrywialne wnętrze, lub jego brak i surowe kopiowanie ma niuanse (kopało mnie bazodanowe connection w frameworku MFC). Regułą 0, 3, 5 itd...
Więc obawy dotyczną mocno "żywych", dynamicznych obiektów, a nie dotyczną prostych danych. Wycofuję to stwierdzenie.

1
srk71 napisał(a):

void pokaz(ostream & out){
...

Możesz podać przykładową definicje tej funkcji przesyłającą konkretne pola na ekran.

Taki zapis zaproponowany przeze mnie daje elastyczność tylko w wyborze strumienia.

Gdyby chcieć selektywnie, to musisz udostępnić getery (znów ta nieładna tablica) i kod kliencki wybierze co jest potrzebne.

Wyobraź sobie, że implementujesz klasę 'Date'. Wiodące jest drukowanie całości na strumień, zwykle operatorem <<.
Plus klasa udostępnia getery getDayOfWeek() itd... i nie wnika kto ich użyje, lub nie u żyje.

2

Może lepiej na przykładzie:

#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>
#include <cmath>
#include <algorithm>
using namespace std;

const string Periods[]={"Ikw","IIkw","IIIkw","IVkw"};
const size_t PeriodCount=sizeof(Periods)/sizeof(*Periods);
//const size_t PeriodCount=size(Periods); // dla nowszych komplatorów

class Money
{
    public:
    //typedef unsigned long long Type;
    using Type = unsigned long long;
    private:
    Type money;
    public:
    explicit Money(double money):money(round(100*money)) {}
    Money(Type money=0):money(money) {}
    friend ostream &operator<<(ostream &s,const Money &m) { return s<<setprecision(2)<<(0.01*m.money); }
    friend istream &operator>>(istream &s,Money &m)
    {
        double val;
        s>>val;
        m=Money(val);
        return s; 
    }
    operator Type()const { return money; }
    explicit operator double()const { return 0.01*money; }
};

class Sales
{
    private:
    string name;
    vector<Money> sales;
    public:
    Sales(string name="brak",const vector<Money> &sales=vector<Money>(PeriodCount,0)):name(name),sales(sales) {}
    friend void show(ostream &os,const Sales &s);
    friend void input(istream &is,Sales &s);
};

void show(ostream &os,const Sales &s)
{
    double smax=(double)(*max_element(begin(s.sales),end(s.sales)));
    double smin=(double)(*min_element(begin(s.sales),end(s.sales)));
    Money sum(0.0);    
    os<<"sprzedawca o nazwisku "<<s.name<<" sprzedal:"<<endl;
    for(size_t i=0;i<PeriodCount;++i)
    {
        sum=Money(sum+s.sales[i]);
        os<<"sprzedaz w "<<Periods[i]<<": "<<s.sales[i]<<endl;        
    }
    os<<"najlepszy kwartal wynik: "<<smax<<endl;
    os<<"najgorszy kwartal wynik: "<<smin<<endl;
    os<<"srednio na kwartal: "<<((double)sum)/PeriodCount<<endl;
}

void input(istream &is,Sales &s)
{
    for(Money &m:s.sales) cin>>m;
    getline(cin>>ws,s.name);
}
 
int main()
{
    vector<Sales> salesmans(2);

    size_t nr=0;
    for(Sales &s:salesmans)
    {
        cout<<"Handlarz "<<(++nr)<<": Podaj "<<PeriodCount<<" wynikow a nastepnie nazwisko sprzedawcy:"<<endl;
        input(cin,s);
    }
    cout<<"---------------"<<endl;
    for(const Sales &s:salesmans)
    {
       show(cout,s);
       cout<<endl;
    }
    return 0;
}

Na pliki sam podzielisz.

Handlarz 1: Podaj 4 wynikow a nastepnie nazwisko sprzedawcy:
1 3 4 6 Kowalski Adam
Handlarz 2: Podaj 4 wynikow a nastepnie nazwisko sprzedawcy:
10 11 2 5 Sikorski Jan
---------------
sprzedawca o nazwisku Kowalski Adam sprzedal:
sprzedaz w Ikw: 1
sprzedaz w IIkw: 3
sprzedaz w IIIkw: 4
sprzedaz w IVkw: 6
najlepszy kwartal wynik: 6
najgorszy kwartal wynik: 1
srednio na kwartal: 3.5

sprzedawca o nazwisku Sikorski Jan sprzedal:
sprzedaz w Ikw: 10
sprzedaz w IIkw: 11
sprzedaz w IIIkw: 2
sprzedaz w IVkw: 5
najlepszy kwartal wynik: 11
najgorszy kwartal wynik: 2
srednio na kwartal: 7
1

Zobacz w jaki sposób można zaimplementować podobną funkcjonalność używając dynamicznych kontenerów, przeciążenia operatora '<<' oraz pętli zakresowych. Wymagany C++11.

#include <iostream>
#include <vector>
#include <map>
#include <limits>

using namespace std;

enum class quarter { I , II , III , IV };

const map<quarter,const string> label { {quarter::I,"I"} , {quarter::II,"II"} , {quarter::III,"III"} , {quarter::IV,"IV"} };

using saledata = vector<pair<quarter,int>>;

class Seller
{
    string name {"Empty"};
    saledata sales;

public:

    Seller( const string& _name = "" , const saledata& _sales = {} ): name {_name} , sales {_sales} {}

    friend ostream& operator<<(ostream&,const Seller&);

};

ostream& operator<<( ostream& os , const Seller& seller )
{
    os << "name = " << seller.name << "\n";
    if( seller.sales.size() == 0 ) return os;

    double average {0.0};
    int max {numeric_limits<int>::min()}, min {numeric_limits<int>::max()};

    for( const auto& sale : seller.sales )
    {
        cout << label.at(sale.first) << " = " << sale.second << "\n";
        average += sale.second;
        if( max<sale.second ) max = sale.second;
        if( min>sale.second ) min = sale.second;
    }

    os << "average value = " << average/seller.sales.size() << "\n";
    os << "minimum value = " << min << "\n";
    os << "maximum value = " << max << "\n";

    return os;
}


int main()
{
    vector<Seller> sellers;

    sellers.emplace_back( "John Smith" , saledata{ { {quarter::I,34} , {quarter::II,23} , {quarter::III,87} , {quarter::IV,9} } } );
    sellers.emplace_back( "Andy" );

    for( const auto& seller : sellers ) cout << seller << "\n\n" ;

    return 0;
}

0

@_13th_Dragon i @TomaszLiMoon ogromne dzięki, to jest bardzo pomocne, mam w końcu konkrety do analizy. Miło, że chciało Wam się to przerobić na coś porządnego. Jeszcze raz dzięki.

1
srk71 napisał(a):

Wcześniej @AnyKtokolwiek napisał, że to jest nieprawidłowe wywoałnie konstruktora, nie rozumiem też dlaczego to działa a to
Sales Sprzedawca[i](nazwisko,sprzedaz); już nie

Pisząc Sales Sprzedawca[5] tworzona jest tablica pięciu instancji klasy Sales i automatycznie wywoływane są domyślne konstruktory. Nie możesz wywołać konstruktora dla już istniejącego obiektu, więc żeby podmienić wybrany obiekt musisz użyć przypisania.

Zapis Sales Sprzedawca[i](nazwisko, sprzedaz) ciężko nawet zinterpretować a co dopiero mówić o działaniu ;)

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