Прошу оценить код 4

Доброго времени суток всем!!!
С Вашего позволения я продолжу начатую мною и оконченную здесь тему.
Все кто уже мало мальски ознакомился с классами прошу скинуть свой e-mail сюда

alf1977@ukr.net

я вышлю исходники и хедер (или в формате .txt как будет Вам удобно) написанной мною программы, дабы получить комментарии, советы , в общем, камни в свой огород :)))
Код не выкладываю в блог по причине объема (около 750 строк). Не уверен «прожует» ли редактор форума такой объем :) В прочем если Selevit разрешит я могу разместить код и здесь.
Череп, Selevit, Алан, porshe как всегда интересно Ваше мнение. Кстати Череп, тема по ссылке окончена на нашем с тобою диалоге и твоего пожелания упаковать мою программу в класс. В общем упаковал...

На первый взгляд — «ошибка» — частое использование system, что ни есть хорошо. Очистку экрана можно( весьма топорно, но ведь работает? ) реализовать так:

void clearConsoleScreen()
{
    HANDLE hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );
    COORD pos;
    pos.X = pos.Y = 0;
    for ( int i = 0; i < 300; i++ )
        cout << endl;
    SetConsoleCursorPosition( hConsoleOutput, pos );
    return; 
}

Правда этот код вряд ли быстр и переносим, но в Windows будет работать, и его быстродействия хватит для рядовых программ.

Больше никаких косяков, заметных мне, не вижу.

Если ты выложишь архив с кодом на файлообменник, всем будет удобнее.

Круто! Только оно у меня не компилится:

Компилятор: TDM-GCC 4.8.1 64-bit Debug
Building Makefile "E:\Dev-Cpp\Projects\PablicServices\Makefile.win"
Выполнение make clean
rm.exe -f obj/mainPablicServices.o obj/PablicServices.o exe/PublicServices.exe

g++.exe -c mainPablicServices.cpp -o obj/mainPablicServices.o -I"E:/Dev-Cpp/MinGW64/include" -I"E:/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"E:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.8.1/include" -I"E:/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.8.1/include/c++" -g3 -std=c++11

In file included from mainPablicServices.cpp:5:0:
PablicServices.h:87:2: error: stray '\361' in program
  void сhangePricesBenefits();
  ^

mainPablicServices.cpp: In function 'int main()':
mainPablicServices.cpp:26:2: error: 'cin' was not declared in this scope
  cin.get();
  ^

mainPablicServices.cpp:26:2: note: suggested alternative:
In file included from PablicServices.h:8:0,
                 from mainPablicServices.cpp:5:
e:\dev-cpp\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.1\include\c++\iostream:60:18: note:   'std::cin'
   extern istream cin;  /// Linked to standard input
                  ^

E:\Dev-Cpp\Projects\PablicServices\Makefile.win:28: recipe for target 'obj/mainPablicServices.o' failed

mingw32-make.exe: *** [obj/mainPablicServices.o] Error 1

Compilation failed after 0.39 seconds with errors

И еще скажи зачем у тебя файлы программы продублированы с расширением txt?

Я вижу, что твой компилятор ругается на то что в файле mainPablicServices.cpp объявлена внешняя переменная класса istream cin. Прошу прощения не включил в файл объявление using std::cin;, хотя у porshe программа скомпилировалась, хз ???
В общем я все изменил, убрал .txt файлы и ссылку на файлообменник поменял.

хотя у porshe программа скомпилировалась, хз ???

у меня было больше ошибок, я их просто молча исправил :)

у меня было больше ошибок, я их просто молча исправил :)

всех обманул )))

Я тоже уже сам поправил баги. Компилятор почему то не захотел кушать русскую «с» в названии функции changePricesBenefits(), и не понял приведение типа в виде if (choice > unsigned int(n)).

Alf, если то что ты выложил это рабочий код, то как он у тебя откомпилировался?

С исходным текстом я еще не разобрался :( Очень много написано.

Мне не понравилось устройство меню. Как то не логично оно устроено. В «расчеты» можно войти без установки «месяца расчета», но в самих расчетах месяц уже установить нельзя — надо выйти в меню на уровень вверх.

Еще мне показалось что программа неправильно считает. Я не разбирался в деталях по исходнику, но мне кажется что параметры «Льгота» и «Лимит по льготе» вообще нигде не учитывается. А по электричеству разве в пределах 150квт не должно считаться по более низкой ставке? То есть например если потратили 200квт, то первые 150квт считать по более низкой ставке, а последующие 50квт — по более высокой?

Еще хорошо бы, что бы в сохраненном отчете сохранялись не только деньги, но и все параметры расчета: показания счетчиков, разность показаний, тариф. И еще, что бы каждый расчет записывался в свой файл, а не затирал предыдущий расчет. Уникальное имя файла можно делать из номера года и номера месяца. Тогда можно будет понять когда, сколько и по каким тарифам заплатили. Думаю что на Украине тарифы на коммуналку тоже растут регулярно :(

Alf, если то что ты выложил это рабочий код, то как он у тебя откомпилировался?

Откомпилировал на VS 2010 и пашет как папа Карло :)))
Но суть в другом, если ты все таки собрал программу, то ты уже второй человек у которого она работает, на мой взгляд, с очень БОЛЬШОЙ и жирной хохмой, но об этом чуть позже...
Ошибку с буквой 'с' исправил.

Мне не понравилось устройство меню. Как то не логично оно устроено. В «расчеты» можно войти без установки «месяца расчета», но в самих расчетах месяц уже установить нельзя — надо выйти в меню на уровень вверх.

В расчеты можно войти без выбора месяца для того чтобы посмотреть предыдущий расчет. Но твоя логика верна, при выборе расчета нужно дать возможность выбрать месяц. Исправил.

Новая ссылка на файлообменник

http://dropmefiles.com/d5uLF

Еще мне показалось что программа неправильно считает.

Все правильно показалось. Я не оговорил, что функция calculations(); не содержит расчетной части, а только умножает разницу на основной тариф (т.к. не это самое главное на данном этапе) в целях просмотра работоспособности :)

OK selevit сюда, так сюда :)
Макар хохма заключается в функции changePricesAndBenefits() в ней вызывается функция saveData которая возвращает тип bool без присваивания возвращаемого значения какой либо переменной ??? Объяснить я этого не могу, но программа работает у меня, у тебя и у porshe.
Макар если будешь собирать программу, то лучше копируй все файлы от сюда, а не с файлообменника, т.к. здесь файлы отредактированы.
Вот такая хохма :)))

Файл PablicServices.h

    // PablicServices.h -- определение класса PablicServices
    // Alf  21.07.2014  22:58

    #ifndef PABLICSERVICES_H_
    #define PABLICSERVICES_H_

    #include <iostream>
    #include <fstream>  // ofstream, ifstream
    #include <cstring>  // strlen() 
    #include <sstream>  // stringstream
    #include <string>
    #include <iomanip>
    #include <cstdlib>

    using std::string;
    using std::ostream;

    //////////////////////////////////// КОНСТАНТЫ /////////////////////////////////////////
    const int PRICES = 11;  // кол-во расчетов (газ, свет, вода)
    const int CALC = 3;     // кол-во цен, льгот и лимитов в массисе prices
    const int MENUS = 5;    // кол-во меню (гл. меню, расчеты, тарифы и льготы, изменить)
    const int LOWER = 75;   // лимит на электричество, кВт
    const int UPPER = 150;  // лимит по договору
    const string MONTHS[] = {"январь", "февраль", "март", "апрель", "май", "июнь",
                            "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"};
    const string ABRRCALC[] = {"газ", "электричеств", "вод"};   // абревиатуры расчетов
    const string ENDINGS[] = {"а", "о", "у", "ы"};      // окончания
    const string DIMS[] = {"м3", "кВт", "грн" , "%"};   // размерности

    /////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////// ОПРЕДЕЛЕНИЕ КЛАССА PablicServices //////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////


    class PablicServices
    {
    private:
        unsigned int choice;    // выбор меню
        unsigned int month;     // выбор месяца
        double prices[PRICES];
        /*  В массиве prices
        0, 1, 2 - цены на газ, свет и воду соотв.,
        3 - цена на свет св. 150 кВт,
        4, 5, 6 - льгота в % на газ, свет и воду соотв.,
        7, 8, 9 - лимит по льготе на газ (зима), свет и воду соотв.
        10 - лимит на газ (лето)                                    */
        double totalsum[CALC];  // общая сумма к оплате за 0 - газ, 1 - свет, 2 - воду

    public:
        // конструктор по умолчанию - устанавливает все элементы данных в 0
        // и вызывает функцию loadPricesAndBenefits, который загружает данные из файла,
        // и передает ей адрес enter
        PablicServices(bool * enter);

        // функция загружает цены из файла prices.txt
        void loadPricesAndBenefits(bool * enter);

        // ввод тарифов и льгот, параметр n номер расчета
        // 0 - газ, 1 - свет, 2 - вода
        void setPricesAndBenefits(double p[], const unsigned int n);

        // функция переходов между меню программы
        void mainMenu(bool * enter);

        // функция отображения меню и выбора пунктов меню
        void getMenus(const int n);

        // функция производит расчеты стоимости газа, света и воды
        void calculations();

        // функция обрабатывает ввод и отображение расчетных данных
        // вызывается из функции calculations, принимает в качестве первого параметра
        // флаг, который сигнализирует о том вводить показания или отображать расчеты
        void inputOutputReadout(const bool flag, unsigned int * diffr);

        // отображение общей суммы за расчетный месяц
        void getTotalSum();

        // функция отображения и сохранения общей суммы расчетов
        // первый параметр функции ссылка на объект класса ostream (cout (для вывода на экран)
        // или fout (для записи в файл "savedcalc.txt")) 
        void outSaveTotalSum(ostream & out, const double sum);

        // функция отображения последнего сохраненного расчета
        void getLastSavedCalc();

        // функция выводит на экран тарифы и льготы
        void getPricesAndBenefits(const double prices[]);

        // функция изменяет тарифы и льготы
        void changePricesAndBenefits();

        // функция сохраняет значения в файле prices.txt 
        // в качестве параметра - временный массив для хранения данных
        bool saveData(double p[]);

        // функция с аргументом по умолчанию обрабатывает корректный ввод выбора (в меню и при запросах о сохранении данных)
        // т.к. функция Input не возвращает отрицательных значений, функция CorrectInput, имеющая параметр
        // число пунктов меню, не позволяет ввести число - большее числа пунктов меню (минимальное кол-во пунктов = 1)
        void correctInput(int n = 1);

        // перегруженные функции, корректно обрабатывают ввод значений типа int и double и возвращают только положительные значения
        void input(unsigned int * intval);
        void input(double * dbval);

        // деструктор
        ~PablicServices();
    };

    #endif

Файл mainPablicServices.cpp

// mainPablicServices.cpp -- главный исполняющий файл
// программы PablicServices
// Alf  23.07.2014  14:10

#include "PablicServices.h"

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;
    using std::setw;
    using std::right;

    setlocale(0, "");

    bool enter = false; // флаг для входа в программу и выхода из нее

    PablicServices data(&enter);        // создание объекта и вызов конструктора по умолчанию

    if (enter)                  // если enter == true ...
        data.mainMenu(&enter);  // ... вход в главное меню

    system ("cls");
    cout << "\n\n\n\n\n" << setw(25) << right << "ДО СВИДАНИЯ\n\n\n\n\n" << "<Enter>" << endl;
    cin.get();

    return 0;
}

Файл PablicServices.cpp

// определения элемент-функций класса PablicServices
// файл содержит реализацию функций, прототипы которых
// объявлены в PablicServices.h
// Alf  23.07.2014  12:21

#include "PablicServices.h"

// конструктор по умолчанию -- устанавливает все элементы данных в 0
// и загружает сохраненные тарифы и льготы при запуске программы
PablicServices::PablicServices(bool * enter)
{
    choice = 0;
    month = 0;
    for (int i = 0; i < PRICES; i++)
        prices[i] = 0;
    for (int i = 0; i < CALC; i++)
        totalsum[i] = 0;
    loadPricesAndBenefits(enter);
}

// функция загрузки сохранненых данных при запуске программы
// параметр enter из функции main() - для входа в гл. меню
void PablicServices::loadPricesAndBenefits(bool * enter)
{
    using namespace std;

    if (!*enter){   // если enter == false - это запуск программы и нужно прочитать тарифы в массив prices...
        ifstream fin;
        fin.open("prices.txt");     // ... из файла "prices.txt"...
        if (!fin.is_open()){            // ... если файла "prices.txt" нет, значит запуск происходит впервые и файл "prices.txt" нужно создать 
            cout << "Отсутствуют сохраненные тарифы.\n"
                << "*** Создать файл \"Тарифы\"? ***\n\n"
                << setw(16) << left << "* Да - 1 *" << setw(16) << right << "* Нет - 0 *\n\n"
                << "Ваш выбор: ";
            do{
                correctInput();
                if (1 == choice){       // 1-создать файл "prices.txt"...
                    system("cls");

                    double p[PRICES];                   // временный массив для хранения тарифов
                    for (int i = 0; i < PRICES; i++)    // инициализация массива отрицательными значениями
                        p[i] = -1;  // таким образом в функции saveData(double p[]) происходит сортировка значений которые >= 0

                    for (unsigned int i = 0; i < CALC; i++){        // вводим значения тарифов и льгот
                        if (0 == i){                    // тарифы и льготы на газ
                            system("cls");
                            cout << "************ ГАЗ *************\n\n";
                            setPricesAndBenefits(p, i);
                        }
                        else if (1 == i){               // тарифы и льготы на электричество
                            system("cls");
                            cout << "******* ЭЛЕКТРИЧЕСТВО ********\n\n";
                            setPricesAndBenefits(p, i);
                        }
                        else {                          // тарифы и льготы на воду
                            system("cls");
                            cout << "************ ВОДА ************\n\n";
                            setPricesAndBenefits(p, i);
                        }
                    }
                    bool save = saveData(p);        // сохранить данные и проверить на успешное сохранение
                    if (save){
                        choice = 0;
                        *enter = true;  // переменной *enter = true, чтобы войти в гл.меню
                        system("cls");
                        cout << "\n\n\n\n\n" << setw(24) << right << "ДОБРО ПОЖАЛОВАТЬ" << "\n\n\n\n\n<Enter>";
                        cin.get();
                        system("cls");
                    }
                }
            }while (choice);
        }
        else {                              // если файл "prices.txt" существует - открыть его...
            int i;

            for (i = 0; fin.good(); i++)    // ... и прочитать тарифы в массив prices
                fin >> prices[i];

            if (fin.eof()){
                cout << "\n\n\n\n\n" << setw(24) << right << "ДОБРО ПОЖАЛОВАТЬ" << "\n\n\n\n\n<Enter>";
                cin.get();
                system("cls");
            }
            else if (fin.fail())
                cout << "Input terminated by data mismatch.\n";
            else
                cout << "Input terminated be unknown reason.\n";
            if (0 == i)
                cout << "\nNo data processed.\n";
            else
                *enter = true;      // переменной *enter = true, чтобы войти в гл.меню
        }
        fin.close();
    }
}

// ввод значений тарифов и льгот
void PablicServices::setPricesAndBenefits(double p[], const unsigned int n)
{
    using std::cout;

    if (0 == n){    // ввод тарифов и льгот на газ
        cout << "Тариф на " << ABRRCALC[n] << ", " << DIMS[n + 2] << ": ";
        input(&p[n]);
        cout << "Льгота, " << DIMS[n + 3] << ": ";
        input(&p[n + 4]);
        cout << "Лимит по льготе (зима), " << DIMS[n] << ": ";
        input(&p[n + 7]);
        cout << "Лимит по льготе (лето), " << DIMS[n] << ": ";
        input(&p[n + 10]);
    }
    else if (1 == n){   // ввод тарифов и льгот на свет
        cout << "Тариф до " << UPPER << " " << DIMS[n] << ", " << DIMS[n + 1] << ": ";
        input(&p[n]);
        cout << "Тариф св " << UPPER << " " << DIMS[n] << ", " << DIMS[n + 1] << ": ";
        input(&p[n + 2]);
        cout << "Льгота, " << DIMS[n + 2] << ": ";
        input(&p[n + 4]);
        cout << "Лимит по льготе, " << DIMS[n] << ": ";
        input(&p[n + 7]);
    }
    else if (2 == n){
        cout << "Тариф на " << ABRRCALC[n] << ENDINGS[n] << ", " << DIMS[n] << ": ";
        input(&p[n]);
        cout << "Льгота, " << DIMS[n + 1] << ": ";
        input(&p[n + 4]);
        cout << "Лимит по льготе, " << DIMS[n - 2] << ": ";
        input(&p[n + 7]);
    }
}

// функция перехода между меню программы
void PablicServices::mainMenu(bool * enter)
{
    bool flags[MENUS] = {0};    // флаги переключения меню 
// 0 - Главное меню, 1 - Выбор месяца, 2 - Расчеты, 3 - Тарифы и льготы, 4 - Изменить

    flags[0] = true;    // установить флаг Главное меню в true

    while (*enter){

        if (flags[0])
            getMenus(0);    // отобразить Главное меню
        else if (flags[1]){
            getMenus(1);        // отобразить Выбор месяца
            if (choice)         // если выбор != 0 переменную choice устанавливаем в 0
                choice = 0;     // чтобы в операторе switch переключить флаги и выйти в гл. меню
        }
        else if (flags[2])
            getMenus(2);        // отобразить меню Расчеты
        else if (flags[3])
            getMenus(3);        // отобразить меню Тарифы и льготы
        else
            getMenus(4);        // отобразить меню Изменить

        switch(choice){
        case 1:
            if (flags[0]){  // Главное меню > Выбор месяца
                flags[0] = false;
                flags[1] = true;
            }
            else if (flags[2]){         // Расчеты > Расчет газа
                calculations();
            }
            else if (flags[3]){         // Тарифы и льготы > Просмотреть
                getPricesAndBenefits(prices);
            }
            else if (flags[4]){         // Изменить > Тарифы и льготы на газ
                changePricesAndBenefits();
            }
            break;
        case 2:
            if (flags[0]){              // Главное меню > Расчеты
                flags[0] = false;
                flags[2] = true;
            }
            else if (flags[2]){         // Расчеты > Расчет электричества
                calculations();
            }
            else if (flags[3]){         // Тарифы и льготы > Изменить
                flags[3] = false;
                flags[4] = true;
            }
            else if (flags[4]){         // Изменить > Тарифы и льготы на электричество
                changePricesAndBenefits();
            }
            break;
        case 3:
            if (flags[0]){              // Главное меню > Тарифы и льготы
                flags[0] = false;
                flags[3] = true;
            }
            else if (flags[2]){ // Расчеты > Расчет воды
                calculations();
            }
            else if (flags[4]){         // Изменить > Тарифы и льготы на воду
                changePricesAndBenefits();
            }
            break;
        case 4:
            if (flags[2]){  // Расчеты > Просмотр и сохранение общей суммы расчета
                getTotalSum();
            }
            break;
        case 5:
            if (flags[2]){  // Расчеты > Последний сохраненный расчет
                getLastSavedCalc();
            }
            break;
        case 0:                         // смена флагов
            if(flags[0]){           // если в гл. меню выбран 0 ...
                flags[0] = false;
                *enter = false;     // ... завершить программу
            }
            else if (flags[1]){     // если в меню Выбор месяца выбран 0 ...
                flags[1] = false;
                flags[0] = true;    // ... перейти в гл. меню
            }
            else if (flags[2]){     // если в меню Расчеты выбран 0 ...
                month = 0;          // ... сбросить значение месяца ...
                for (int i = 0; i < CALC; i++)  // расчеты в totalsum - сбросить в 0
                    totalsum[i] = 0;
                flags[2] = false;
                flags[0] = true;    // ... перейти в гл. меню
            }
            else if (flags[3]){     // если в меню Тарифы и льготы выбран 0 ...
                flags[3] = false;
                flags[0] = true;    // ... перейти в гл. меню
            }
            else if (flags[4]){     // если в меню Изменить выбран 0 ...
                flags[4] = false;
                flags[3] = true;    // ... перейти в меню Тарифы и льготы
            }
            break;
        }
    }   // конец цикла while выход из гл. меню и из программы (enter == false)
}

// функция отображения меню и выбора пунктов меню
void PablicServices::getMenus(const int n)
{
    using namespace std;

    if (0 == n){        // Главное меню
        system("cls");
        cout << "******** ГЛАВНОЕ МЕНЮ ********\n\n"
                "1. Mесяц расчета\n2. Расчеты\n3. Тарифы и льготы\n0. Выход\n\nВаш выбор: ";

        correctInput(3);
    }
    else if (1 == n){ // Выбор месяца
        system("cls");

        unsigned int temp = choice; // временная переменная для сохранения текущего значения choice
            // в функции calculations значение choice используется для выбора расчета (газ, свет или вода)
            // но при выборе месяца значение теряется т.к. функция input привязана к значению choice

        for (int i = 0; i < CALC; i++)  // при выборе месяца все расчеты в totalsum - сбрасываються в 0
            totalsum[i] = 0;

        do{ 
            cout << "******** ВЫБОР МЕСЯЦА ********\n\n";
            for (int i = 0; i < 6; i++){
                cout << setw(2) << right << (i + 1) << ". " << setw(14) << left << MONTHS[i]
                << setw(2) << right << (i + 7) << ". " << setw(14) << left << MONTHS[i + 6] << endl;
            }
            cout << setw(19) << right << "0. Вернуться" << "\n\nВаш выбор: ";

            correctInput(12);

            month = choice;
            choice = temp;

        }while (month && month > 12);
    }
    else if (2 == n){       // меню Расчеты
        system("cls");
        cout << "Расчет оплаты коммунальных услуг";
        if (month)      // если не выбран расчетный месяц - месяц не отображать 
            cout << " за " << MONTHS[month - 1];
        cout << "\n\n1. Расчет газа\n2. Расчет электричества\n3. Расчет воды\n4. Общая сумма";
        if (month)      // если не выбран расчетный месяц - месяц не отображать
            cout << " за " << MONTHS[month - 1];
        cout << "\n5. Предыдущий сохраненный расчет\n0. Главное меню\n\nВаш выбор: ";

        correctInput(5);
    }
    else if (3 == n){       // меню Тарифы и льготы
        system("cls");
        cout << "****** ТАРИФЫ И ЛЬГОТЫ *******\n\n1. Просмотреть\n2. Изменить\n0. Главное меню\n\nВаш выбор: ";

        correctInput(2);
    }
    else if (4 == n){       // меню Изменить
        system("cls");
        cout << "********** ИЗМЕНИТЬ **********\n\n1. Тарифы и льготы на газ\n2. Тарифы и льготы на электричество\n"
                "3. Тарифы и льготы на воду\n0. Меню тарифы и льготы\n\nВаш выбор: ";

        correctInput(3);
    }
}

// функция производит расчеты стоимости газа, света и воды
void PablicServices::calculations()
{
    using std::cout;
    using std::cin;

    if (!month){    // если не выбран месяц - показать меню выбора месяца
        system("cls");
        getMenus(1);
    }
    if (month){
        unsigned int diffr = 0; // разница между текущим и предыдущим показаниями
        bool flag = true;   // переключение ввода и отображения в функции inputOutputReadout
        inputOutputReadout(flag, &diffr);   // вызов функции для ввода показаний
                                            // и вычисления разницы flag == true
        if (1 == choice){   // расчет газа
            totalsum[choice - 1] = diffr * prices[choice - 1];
        }
        else if (2 == choice){  // расчет света
            if (diffr > 150)
                totalsum[choice - 1] = diffr * prices[choice + 1];
            else
                totalsum[choice - 1] = diffr * prices[choice - 1];
        }
        else if (3 == choice){  // расчет воды
            totalsum[choice - 1] = diffr * prices[choice - 1];
        }

        flag = false;
        inputOutputReadout(flag, &diffr);   // эта же функция выводит на экран
                                            // значения расчетов flag == false
        cin.get();
    }
}   // конец функции Calculation

// функция обрабатывает ввод и отображение расчетных данных
// вызывается из функции calculations, принимает в качестве первого параметра
// флаг, который сигнализирует о том вводить показания или отображать расчеты
void PablicServices::inputOutputReadout(const bool flag, unsigned int * diffr)
{
    using namespace std;

    unsigned int current = 0, previous = 0;
    if (flag){  // если flag == true - ввод показаний
        do{
            system("cls");
            cout << "Расчет стоимости " << ABRRCALC[choice - 1] // если выбран расчет газа или электричества показать окончание "а" ...
            << ((1 == choice || 2 == choice) ? ENDINGS[0] : ENDINGS[3]) // ... а если расчет воды - то окончание "ы"
            << " за " << MONTHS[month - 1] << "\n\n"
            "Текущее показание: ";
            input(&current);
            cout << "Предыдущее показание: ";
            input(&previous);
            if (current < previous){        // проверка - первое показание должно быть > второго
                system("cls");
                cout << "Текущее показание не может быть\n"
                    "меньше чем предыдущее!!!\n"
                    "Введите корректные показания...\n\n"
                    "<Enter>";
                cin.get();
            }
        }while (current < previous);
    *diffr = current - previous;
    }
    else if(!flag){ // если flag == false - отображение произведенных расчетов
        cout << "\nИзрасходовано: " << setw(9) << right << *diffr << " " << ((1 == choice || 3 == choice) ? DIMS[0] : DIMS[1]) << '\n';
        if (2 != choice){
            cout << "Тариф: " << setw(17) << right << setiosflags(ios::fixed) << setprecision(4) << prices[choice - 1] << " " << DIMS[2] << '\n';
        }
        else if (2 == choice){
            if (*diffr > 150)
                cout << "Тариф: " << setw(17) << right << setprecision(4) << prices[choice + 1] << " " << DIMS[2] << '\n';
            else
                cout << "Тариф: " << setw(17) << right << setprecision(4) << prices[choice - 1] << " " << DIMS[2] << '\n';
        }
        cout << "Сумма оплаты: " << setw(10) << right << setprecision(2) << totalsum[choice - 1] << " " << DIMS[2] << "\n\n"
            "<Enter>";
    }
}

// отображение общей суммы за расчетный месяц
void PablicServices::getTotalSum()
{
    using namespace std;

    double sum = 0;
    for (int i = 0; i < CALC; i++)
        sum += totalsum[i];     // если в массиве totalsum есть данные ...
    if (sum){                           // ... вывести их на экран или сохранить
        system("cls");
        outSaveTotalSum(cout, sum);                     // вывод на экран общей суммы расчета
        cout << "\n********  Сохранить?  ********\n"    // запрос на сохранение
            << setw(15) << left << "* Да - 1 *" << setw(15) << right << "* Нет - 0 *" << "\n\nВаш выбор: ";
        do{
            correctInput();
            if (1 == choice){       // если choice == 1 - сохранить данные
                ofstream fout;
                fout.open("savedcalc.txt");

                outSaveTotalSum(fout, sum);     // запись общей суммы расчета в файл "savedcalc.txt"

                cout << "\n\nФайл успешно сохранен.\n<Enter>";
                fout.close();
                cin.get();
                break;
            }
        }while(choice);     // если choice == 0 - не сохранять данные
    }
    else{       // если в массиве totalsum нет данных - значит нужно сделать расчет
        system("cls");
        cout << "Вы ничего не рассчитали.\nСделайте расчет пожалуйста...\n\n<Enter>";
        cin.get();
    }
}

// функция отображения и сохранения общей суммы расчетов
// первый параметр функции ссылка на объект класса ostream (cout (для вывода на экран)
// или fout (для записи в файл "savedcalc.txt")) 
void PablicServices::outSaveTotalSum(ostream & out, const double sum)   
{
    using namespace std;

    out << "Сумма расчетов за " << MONTHS[month - 1] << "\n\n" <<
        "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
        << setw(21) << left << "Газ, грн" << setw(9) << right << setiosflags(ios::fixed) << setprecision(2) << totalsum[0] << "\n"
        << setw(21) << left << "Электроэнергия, грн" << setw(9) << right << totalsum[1] << "\n"
        << setw(21) << left << "Вода, грн" << setw(9) << right << totalsum[2] << "\n"
        << "------------------------------\n"
        << setw(21) << left <<  "Итого" << setw(9) << right << sum << endl;

}

// функция отображения последнего сохраненного расчета
void PablicServices::getLastSavedCalc()
{
    using namespace std;

    ifstream fin;
    fin.open("savedcalc.txt");
    if (!fin.is_open()){    // если файла "savedcalc.txt" не существует - сообщение о том, что нужно сделать расчет
        system("cls");
        cout << "Нет сохраненного файла.\nСделайте расчет и сохраните файл.\n\n<Enter>";
    }
    else {
        system("cls");
        char ch;
        int count = 0;
        while (fin.get(ch)){    // чтение файла...
            cout << ch;         // ... и вывод его на экран
            ++count;
        }
        if (fin.eof())
            cout << "\n\n<Enter>";
        else if (fin.fail())
            cout << "Input terminated by data mismatch.\n";
        else
            cout << "Input terminated by unknown reason.\n";
        if (0 == count)
            cout << "\nNo data processed.\n";
    }
    fin.close();
    cin.get();
}

// функция выводит на экран тарифы и льготы
void PablicServices::getPricesAndBenefits(const double prices[])
{
    using namespace std;

    system("cls");
    cout << setw(15) << right << '|' << setw(16) << right << "Тариф, грн" << '|' << setw(12) << right
        << "Льгота, %" << '|' << setw(18) << right << "Лимит по льготе"
        << "\n---------------------------------------------------------------\n";
    for (int i = 0; i < CALC; i++){
        if (0 == i){        // отображение поля газ
            cout << setw(14) << left << "Газ" << '|' << setw(16) << right << setiosflags(ios::fixed) << setprecision(4) << prices[i] << '|'
                << setw(12) << right << setprecision(0) << prices[i + 4] << '|' << setw(4) << right << "зима " << setw(10) << right
                << setprecision(0) << prices[i + 7] << " " << DIMS[i] << '\n' << setw(15) << '|' << setw(17) << '|' << setw(13)
                << '|' << setw(4) << right << "лето " << setw(10) << right << setprecision(0) << prices[i + 10] << " " << DIMS[i]
                << "\n---------------------------------------------------------------\n";
        }   // конец поля газ
        else if (1 == i){   // отображение поля электричество
            cout << setw(14) << left << "Электричество" << '|' << setw(2) << left << "до" << UPPER << DIMS[i] << setw(8)
                << right << setprecision(4) << prices[i] << '|' << setw(12) << right << setprecision(0) << prices[i + 4]
                << '|' << setw(14) << right << setprecision(0) << prices[i + 7] << " " << DIMS[i] << '\n' << setw(15) << '|'
                << setw(2) << "св" << UPPER << DIMS[i] << setw(8) << right << setprecision(4) << prices[i + 2] << '|' << setw(13) << right << '|' << setw(18)
                << "\n---------------------------------------------------------------\n";
        }   // конец поля электричество
        else{               // отображение поля вода
            cout << setw(14) << left << "Вода" << '|' << setw(16) << right << prices[i] << '|'
                << setw(12) << right << setprecision(0) << prices[i + 4] << '|' << setw(15) << right << setprecision(2) << prices[i + 7]
                << " " << DIMS[i - 2] << "\n---------------------------------------------------------------" << endl;
        }   // конец поля вода
    }
    cout << "\n<Enter>";
    cin.get();
}

// функция изменяет тарифы и льготы
void PablicServices::changePricesAndBenefits()
{
    using namespace std;

    system("cls");

    double p[PRICES];                   // временный массив для хранения тарифов
    for (int i = 0; i < PRICES; i++)    // инициализация массива отрицательными значениями
        p[i] = -1;  // таким образом в функции saveData(double p[]) происходит сортировка значений которые >= 0

    if (1 == choice){
        cout << "******** ИЗМЕНИТЬ ГАЗ ********\n\n"
            "Текущий тариф, " << DIMS[choice + 1] << ": " << setw(15) << right << setiosflags(ios::fixed) << setprecision(4) << prices[choice - 1]
            << "\nТекущая льгота, " << DIMS[choice + 2] << ": " << setw(16) << right << setprecision(0) << prices[choice + 3]
            << "\nЛимит по льготе (зима), " << DIMS[choice - 1] << ": " << setw(7) << right << setprecision(0) << prices[choice + 6]
            << "\nЛимит по льготе (лето), " << DIMS[choice - 1] << ": " << setw(7) << right << setprecision(0) << prices[choice + 9] << "\n" << endl;
        setPricesAndBenefits(p, choice - 1);
        saveData(p);    // !!! хохма -- функция имеет возвращаемый тип bool
    }
    else if (2 == choice){
        cout << "*** ИЗМЕНИТЬ ЭЛЕКТРИЧЕСТВО ***\n\n"
            "Текущий тариф до " << UPPER << " " << DIMS[choice - 1] << ", " << DIMS[choice] << ": " << setw(10) << right
            << setiosflags(ios::fixed) << setprecision(4) << prices[choice - 1]
            << "\nТекущий тариф св " << UPPER << " " << DIMS[choice - 1] << ", " << DIMS[choice] << ": " << setw(10) << right << prices[choice + 1]
            << "\nТекущая льгота, " << DIMS[choice + 1] << ": " << setw(22) << right << setprecision(0) << prices[choice + 3]
            << "\nЛимит по льготе, " << DIMS[choice - 1] << ": " << setw(19) << right << setprecision(0) << prices[choice + 6] << "\n" << endl;
        setPricesAndBenefits(p, choice - 1);
        saveData(p);    // !!! хохма -- функция имеет возвращаемый тип bool
    }
    else if (3 == choice){
        cout << "******** ИЗМЕНИТЬ ВОДА ********\n\n"
            "Текущий тариф, " << DIMS[choice - 1] << ": " << setw(11) << right << setiosflags(ios::fixed) << setprecision(4) << prices[choice - 1]
            << "\nТекущая льгота, " << DIMS[choice] << ": " << setw(12) << right << setprecision(0) << prices[choice + 3]
            << "\nЛимит по льготе, " << DIMS[choice - 1] << ": " << setw(9) << right << setprecision(2) << prices[choice + 6] << "\n" << endl;
        setPricesAndBenefits(p, choice - 1);
        saveData(p);    // !!! хохма -- функция имеет возвращаемый тип bool
    }
}   // конец функции сhangePricesBenefits();

// функция сохраняет значения в файле prices.txt
bool PablicServices::saveData(double p[])
{
    using namespace std;

    cout << "\n********  Сохранить?  ********\n\n"  // запрос на сохранение значений
        << setw(15) << left << "* Да - 1 *" << setw(15) << right << "* Нет - 0 *" << "\n\nВаш выбор: ";

    correctInput();
    if (1 == choice){   // сохранить значения тарифов и льгот
        ofstream fout;
        fout.open("prices.txt");
        fout.setf(ios_base::fixed, ios_base::floatfield);
        fout.precision(4);
        for (int i = 0; i < PRICES; ++i){   // записываем значения в массив prices
            if (p[i] >= 0)              // если элемент временного массива >= 0
                prices[i] = p[i];   // присвоить новое значение элементу массива prices
            fout << prices[i] << '\n';
        }
        fout.close();
        system("cls");
        cout << "Файл успешно сохранен.\n\n"
            "Изменить тарифы и льготы можно в\nГлавное меню > \"Тарифы и льготы\"\n\n<Enter>";
        cin.get();
        return true;
    }
    else{
        cout << "\nФайл не сохранен.\n\n<Enter>";
        cin.get();
        return false;
    }
}   // конец функции saveData();

// функция корректного ввода в меню
void PablicServices::correctInput(int n)
{
    using std::cout;

    do{
        input(&choice);
        if (choice > unsigned int(n))
            cout << "Сделайте корректный ввод >>> ";
    }while (choice > unsigned int(n));
}       // конец функции correctInput(int n);

// перегруженная функция корректно обрабатывает ввод значений типа int и возвращает только положительные значения
void PablicServices::input(unsigned int * intval)
{
    using namespace std;

    char ch;
    char input[256];
    stringstream ss;
    bool correct;

    do{
        correct = true;
        cin.getline(input, 256);

        ss.str(input);

        if (0 == strlen(input)) 
            correct = false;
        else if ((!(ss >> *intval)) || (ss >> ch))
            correct = false;
        ss.clear();
        ss.sync();
        if (!correct || signed int(*intval) < 0)
            cout << "Сделайте корректный ввод >>> ";
    }while (!correct || signed int(*intval) < 0);
}

// перегруженная функция корректно обрабатывает ввод значений типа double и возвращает только положительные значения
void PablicServices::input(double * dbval)
{
    using namespace std;

    char ch;
    char input[256];
    stringstream ss;
    bool correct;

    do{
        correct = true;
        cin.getline(input, 256);

        for (int i = 0; input[i]; i++){
            if (',' == input[i])
                input[i] = '.';     // замена запятой на точку
        }

        ss.str(input);

        if (0 == strlen(input)) 
            correct = false;
        else if ((!(ss >> *dbval)) || (ss >> ch))
            correct = false;
        ss.clear();
        ss.sync();
        if (!correct || *dbval < 0)
            cout << "Сделайте корректный ввод >>> ";
    }while (!correct || *dbval < 0);
}

// деструктор
PablicServices::~PablicServices()
{
}

Alf, никакой хохмы в игнорировании значения, возвращаемого функцией, нет. Это обычное дело. Между прочим, функция printf тоже возвращает значение типа int — количество выведенных символов или код ошибки. Однако printf("Hello, World!) работает ;)

У тебя хохма начнется, если changePricesAndBenefits() почему то не сможет записать записать данные в файл. Тогда будешь мучительно отлаживать, помня о том, что проверка на ошибку записи у тебя в saveData есть.

Alf, вопрос первый: а ты сам доволен своим кодом? Я понимаю, что для курицы только что снесённое яйцо всегда золотое. Но вот прошло немного времени, уровень эндорфинов приблизился к норме... ну, честно, доволен?

Вопрос второй. На сколько просто было отлаживать эту программу? На сколько просто вносить в неё изменения? А через пол-года?

Когда я первый раз смотрел исходник, у меня был шок. Такое впечатление, что ты нарушил все писанные и неписанные правила ООП. Попробуем разобраться...

Идеологические ошибки

(1) Программа плохо спроектирована. Перед тем, как писать код, надо сесть и очень хорошо подумать. Составить тех. задание (ТЗ). Возможно нарисовать схему основных потоков управления и основных потоков данных. Выделить сущности, которые имеют определённую функциональность, с прицелом реализовать их в виде классов. Определить взаимодействия этих сущностей, т.е. прикинуть набор методов для классов. Ну и так далее... (На эту тему не одна книга написана, так что я с этим абзацем закругляюсь.)

(2) Декомпозиция нужна для разделения большой сложной задачи на более мелкие, более простые и более понимаемые задачи. И ООП — хороший инструмент для этого. Твой класс PablicServices не помогает сделать задачу более простой. В данном случае класс — просто обёртка вокруг обычного процедурного кода с использованием глобальных переменных.

(3) То, что называется "бизнес-логика" должно быть, как минимум, отделено от интерфейса пользователя. Представь себе, что консольное приложение ты соберёшься переделать в GUI-приложение. Если по-хорошему, то надо будет сделать только GUI: остальное уже сделано и отлажено.

(4) Есть такое правило хорошего стиля программирования: функция должна умещаться на одном экране. Оно, конечно, опциональное и эмпирическое... Но когда я вижу PablicServices::mainMenu() — это правило невольно вспоминается.

(5) Повсеместное использование «магических чисел».

    cout << "Льгота, " << DIMS[n + 1] << ": ";
    input(&p[n + 4]);
    cout << "Лимит по льготе, " << DIMS[n - 2] << ": ";
    input(&p[n + 7]);

Что означают эти числа. И почему в одном случае написано DIMS[n + 3], а в другом DIMS[n + 1], хотя в обоих случаях имеется ввиду ссылка на одну и ту же строку в массиве DIMS?

(6) Местами очень запутанный код. Например в логическом спагетти PablicServices::mainMenu() я так до конца и не разобрался, несмотря на наличие комментариев :(

Технические ошибки и недочёты

(1) Заголовочный файл не должен содержать ничего, кроме объявлений. Т.е. компиляция заголовочного файла не должна порождать ни кода, ни данных. Поэтому раздел констант из PablicServices.h надо скопировать в PablicServices.cpp, а в PablicServices.h убрать все инициализаторы и везде добавить спецификатор extern.

(2) Деструктор в классе PablicServices не нужен.

(3) Конструктор предназначен для инициализации объекта. У тебя, помимо инициализации переменных, в конструкторе стоит вызов функции loadPricesAndBenefits(enter), которая реализует добрую треть функциональности программы (не объекта). И к тому же в ней есть куча мест, чреватая нештатными ситуациями (файловый ввод-вывод, взаимодействие с пользователем и пр.) Таких вещей лучше избегать.

TO DO

(1) Вернуться на этап проектирования.

(1.1) Разделить пользовательский интерфейс и бизнес-логику.

(1.2) Выделить сущности, готовые жить собственной жизнью.

Это может быть, например, тариф на ресурс, включая льготы и пр., что необходимо для расчёта, а также сам расчёт. В этом случае в качестве полезного выхода имеем либо одно число (посчитанные деньги), либо другой объект, типа «результат», содержащий не только деньги, но и показания счётчика, номер месяца и т.п.

Это также может быть и объект, содержащий не только «тариф» (см. выше), но и посчитанный результат. Тогда с одной стороны увеличиваем сложность объекта, а с другой — улучшаем функциональность: в качестве результата объект может выдать пользователю (и в файл журнала!) не только сумму, но и полную информацию по использовавшемуся тарифу (тарифы меняются).

(1.3) Спроектировать пользовательский интерфейс.

(2) По результатам (1) переписать программу, используя имеющиеся наработки.

(3) Оценить дело рук своих. В случае неудовлетворения вернуться к (1).

Программирование — процесс итерационный. В замкнутой системе программист-программа количество итераций определяется только степенью перфекционизма программиста.

Alf, вопрос первый: а ты сам доволен своим кодом?

На момент написания этого класса, я прочитал первую главу из раздела «Классы» — «Раздельная компиляция» (Стивен Пратта). Естественно руки зачесались и захотелось творить:).
Сначала хотел взять за основу свой последний вариант, но оказалось не все так просто.
Заново разложил все по полочкам нарисовал схему взаимодействия, но когда стал писать, то тут, то там стали вылезать разные косяки. Закралась мысль, которую я сейчас обдумываю, — нужно разбить некоторые функции на более мелкие, создать наверное три класса, которые будут отвечать за интерфейс, расчетную часть и за вывод, чтение и сохранение данных, данные о тарифах и льготах, которые хранятся в одном массиве, разделить (отдельно газ, свет, вода).
В этот момент на форуме была дискуссия в которой упоминалась литература. Начал читать Дейтела (кстати говоря, как по мне, для начинающего программиста, у которого нет вообще никакого понятия о программировании, эта книга произведет эффект нейтронной бомбы:) Я думаю — должна быть хоть какая-то база, а тут сразу классы, объекты ...). Но для меня очень кстати, что книга начинается с нужной мне темы. Прочитал три первых главы (функции setтеры и getтеры) и мое подозрение укрепилось. Теперь зреет другой замысел (аля перфекционизм!).
В общем, ответ такой — кодом не доволен.
Единственное что в нем порадовало — это девяностопятипроцентная защита от «дурака» (защиту от ввода EOF я не вставлял, что в принципе легко исправить).

Вопрос второй. На сколько просто было отлаживать эту программу? На сколько просто вносить в неё изменения?

В принципе в отладке проблем не было, но через пол года не знаю, будет время загляну в программу — отпишусь:)))
А вот на счет изменений — это и сейчас видно, что все функции зависят одна от другой, у меня сложилось впечатление, что они нанизаны как на нитку с иголкой и если нитку порвать то все разлетится как бусы.
В общем у меня в голове очень много задумок по поводу этой программы, но мало знания и опыта о том как это реализовать. Продолжаем обучение.
To be continued...

СПАСИБО, Череп, за комментарии и за Дейтела, я думаю он мне поможет:)

Кстати, объясни пожалуйста как функция которая возвращает значение bool, может вызываться не присваивая возвращаемое значение. В программе это функция saveData, а вызывается она в функции changePricesAndBenefits.

На счёт возвращаемого значения (функция saveData) тебе Макар вроде ответил. Возвращаемое значение игнорируется.

Если посмотреть ассемблерный код (у меня сейчас компилятор TDM-GCC 4.8.1 32-bit, Win), то будет видно, что результат функции возвращается через регистр EAX. В вызывающем коде значение в регистре EAX после вызова функции просто не используется.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.