►BUDGET2...348

Программа BUDGET2 представляет собой результат преобразования программы BUDGET1, основанной на использовании функций, в объектно-ориентированную программу с применением классов.

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

[Атас!]

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

Чековый счёт:

■■■

■ удерживать 20 центов за каждый обработанный чек, если баланс падает ниже 500 долларов;

■ не удерживать 20 центов, если баланс больше 500 долларов.

■■■

Сберегательный счёт:

■■■

■ не удерживать денег при первом снятии со счёта за месяц;

■ удерживать 5 долларов за каждое последующее снятие.

■■■

Рассматривая эту задачу, можно сразу отметить, что главными кандидатами на роль классов являются Checking и Savings. Поскольку данные-члены лучше сделать защищёнными, нам понадобится несколько функций, обеспечивающих доступ к номеру и балансу счёта.

Как и любой класс, Checking и Savings нуждаются в конструкторе, чтобы проинициализировать объекты правильными значениями ( как минимум, обнулить баланс ). Кроме того, понадобятся ещё две функции — deposit( ) ( вклад ) и withdrawal( ) ( снятие ).

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

Вот текст этой программы:

    /* BUDGET2.CPP — программа бюджета, основанная на классах */

    #include <cstdio>

    #include <cstdlib>

    #include <iostream>

_________________

348 стр. Часть 6. Великолепная десятка

    using namespace std ;

    /* Максимальное количество счетов */

    const int maxAccounts = 10 ;

    /* Checking — здесь описан чековый счёт */

    class Checking

    {

    public :

        Checking( int initializeAN = 0 )

             :accountNumber( initializeAN ) , balance( 0.0 ){ }

        /* Функции обращения */

        int accountNo( )

        {

            return accountNumber ;

        }

        double acntBalance( )

        {

            return balance ;

        }

        /* Функции транзакций */

        void deposit( double amount )

        {

            balance += amount ;

        }

        void withdrawal( double amount ) ;

        /* Функция вывода объекта в cout */

        void display( )

        {

            cout << "Счёт " << accountNumber

                 << " = " << balance

                 << " " ;

        }

    protected :

        unsigned accountNumber ;

        double balance ;

    } ;

    /* withdrawal — эта функция-член слишком */

    /*             велика для inline-функции */

    void Checking::withdrawal( double amount )

    {

        if ( balance < amount )

        {

            cout << "Недостаточно денег: баланс равен "

                 << balance

                 << ", сумма чека равна " << amount

                 << " " ;

        }

        else

        {

            balance -= amount ;

            /* Если баланс падает слишком низко... */

            if ( balance < 500.00 )

            {

_________________

349 стр. Глава 31. Программа BUDGET

             /* ...удержать деньги за обслуживание */

                 balance -= 0.20 ;

             }

        }

    }

    /* Savings — вы и сами можете написать этот класс */

    class Savings

    {

    public :

        Savings( int initialAN = 0 )

            : accountNumber( initialAN ) ,

              balance( 0.0 ) , noWithdrawals( 0 ) { }

        /* функции обращения */

        int accountNo( )

        {

            return accountNumber ;

        }

        double acntBalance( )

        {

            return balance ;

        }

        /* функции транзакций */

        void deposit( double amount )

        {

            balance += amount ;

        }

        void withdrawal( double amount ) ;

        /* Функция display — отображает объект */

        void display( )

        {

            cout << "Счёт " << accountNumber

                 << " = " << balance

                 << " ( номер снятия = "

                 << noWithdrawals

                 << " ) " ;

        }

    protected :

        unsigned accountNumber ;

        double balance ;

        int noWithdrawals ;

    } ;

    void Savings::withdrawal( double amount )

    {

        if ( balance < amount )

        {

            cout << "Недостаточно денег на счёте: "

                 << "баланс равен " << balance

                 << ", снимается " << amount

                 << " " ;

        }

        else

        {

        /* После первого в месяце снятия денег... */

_________________

350 стр. Часть 6. Великолепная десятка

            if ( ++noWithdrawals > 1 )

            {

            /* ...удерживать $5 */

            balance -= 5.00 ;

            }

            /* Снять деньги */

            balance -= amount ;

        }

    }

    /* Объявление прототипов */

    void process( Checking* pChecking ) ;

    void process( Savings* pSavings ) ;

    /* Объекты чековых и сберегательных счетов */

    Checking* chkAcnts[ maxAccounts ] ;

    Savings* svgAcnts[ maxAccounts ] ;

    /* main — собирает и выводит данные */

    int main( int argcs , char* pArgs[ ] )

    {

        setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */

        /* Повторять цикл до ввода 'X' или 'х' */

        int noChkAccounts = 0 ; /* Содержит количество счетов */

        int noSvgAccounts = 0 ;

        char accountType ; /* Тип счёта — 'S' или 'С' */

        while ( 1 )

        {

            cout << "Введите S для сберегательных счетов, " << " "

                 << "С для чековых, "

                 << "X для выхода:" ;

            cin >> accountType ;

        /* Выйти из цикла, если пользователь введёт X */

        if ( accountType == 'x' || accountType == 'X' )

        {

            break ;

        }

        /* В противном случае обрабатывать соответствующий счёт */

        switch ( accountType )

        {

        /* чековые счета */

            case 'c' :

            case 'C' :

            if ( noChkAccounts < maxAccounts )

            {

                int acnt ;

                cout << "Введите номер счёта:" ;

                cin >> acnt ;

                chkAcnts[ noChkAccounts ] = new Checking( acnt ) ;

                process( chkAcnts[ noChkAccounts ] ) ;

                noChkAccounts++ ;

            }

            else

            {

                cout << "Для чековых счетов больше нет места " ;

_________________

351 стр. Глава 31. Программа BUDGET

            }

            break ;

            /* сберегательные счета */

            case 's' :

            case 'S' :

            if ( noSvgAccounts < maxAccounts )

            {

                int acnt ;

                cout << "Введите номер счёта:" ;

                cin >> acnt ;

                svgAcnts[ noSvgAccounts ] = new Savings( acnt ) ;

                process( svgAcnts[ noSvgAccounts ] ) ;

                noSvgAccounts++ ;

            }

            else

            {

                cout << "Для сберегательных счетов "

                     << "больше нет места " ;

            }

            break ;

            default :

                cout << "Непонятный символ... " ;

            }

        }

        /* А теперь показать общую сумму */

        double chkTotal = 0 ;

        cout << "Чековые счета: " ;

        for ( int i = 0 ; i < noChkAccounts ; i++ )

        {

            chkAcnts[ i ]  ->  display( ) ;

            chkTotal += chkAcnts[ i ]  ->  acntBalance( ) ;

        }

        double svgTotal = 0 ;

        cout << "Сберегательные счета: " ;

        for ( int j = 0 ; j < noSvgAccounts ; j++ )

        {

            svgAcnts[ j ]  ->  display( ) ;

            svgTotal += svgAcnts[ j ]  ->  acntBalance( ) ;

        }

        double total = chkTotal + svgTotal ;

        cout << "Сумма по чековым счетам = "

             << chkTotal

             << " " ;

        cout << "Сумма по сберегательным счетам = "

             << svgTotal

             << " " ;

        cout << "Общая сумма    = "

             << total

             << " " ;

_________________

352 стр. Часть 6. Великолепная десятка

        /* Пауза для того, чтобы посмотреть на результат работы программы */

        system( "PAUSE" ) ; return 0 ;

    }

    /* обработка( Checking ) — ввод данных по чековым счетам */

    void process( Checking* pChecking )

    {

        cout << "Введите положительное число для вклада, "

             << "отрицательное для снятия, 0 для завершения " ;

        double transaction ;

        do

        {

            cout << ":" ;

            cin >> transaction ;

            // Вклад

            if ( transaction > 0 )

            {

                pChecking -> deposit( transaction ) ;

            }

            // Снятие

            if ( transaction < 0 )

            {

                pChecking -> withdrawal( -transaction ) ;

            }

        } while ( transaction != 0 ) ;

    }

    /* обработка( Savings ) — ввод данных для сберегательных счетов */

    void process( Savings* pSavings )

    {

        cout << "Введите положительное число для вклада, "

             << "отрицательное для снятия, 0 для завершения " ;

        double transaction ;

        do

        {

            cout << ":" ;

            cin >> transaction ;

            // Вклад

            if ( transaction > 0 )

            {

                pSavings -> deposit( transaction ) ;

            }

            // Снятие

            if ( transaction < 0 )

            {

                pSavings -> withdrawal( -transaction ) ;

            }

        } while ( transaction != 0 ) ;

    }

_________________

353 стр. Глава 31. Программа BUDGET 

Я запустил эту программу с приведёнными ниже данными для того, чтобы продемонстрировать, как она работает. Жирным шрифтом выделен пользовательский ввод, а обычным представлены сообщения программы.

    Введите S для сберегательных счетов,

            С для чековых, X для выхода:S

    Введите номер счёта:123

    Введите положительное число для вклада,

    отрицательное для снятия, 0 для завершения

    : 200

    : -20

    : 0

    Введите S для сберегательных счетов,

            С для чековых, X для выхода:S

    Введите номер счёта:234

    Введите положительное число для вклада,

    отрицательное для снятия, 0 для завершения

    : 200 

    : -10

    : -10

    : 0

    Введите S для сберегательных счетов,

            С для чековых, X для выхода:С

    Введите номер счёта:345

    Введите положительное число для вклада,

    отрицательное для снятия, 0 для завершения

    : 200

    : -20

    : 0

    Введите S для сберегательных счетов,

            С для чековых, X для выхода:С

    Введите номер счёта:456

    Введите положительное число для вклада,

    отрицательное для снятия, 0 для завершения

    : 600

    : -20

    : 0

    Введите S для сберегательных счетов,

            С для чековых, X для выхода:Х

    Чековые счета:

    Счёт 345 = 179.8

    Счёт 456 = 580

    Сберегательные счета:

    Счёт 123 = 180 ( номер снятия = 1 )

    Счёт 234 = 175 ( номер снятия = 2 )

    Сумма по чековым счетам = 759.8

    Сумма по сберегательным счетам = 355

    Общая сумма      = 1114.8

    Press any key to continue...

Рассмотрим каждую из функций-членов, начиная с класса Checking. Конструктор присваивает счёту его номер. Значение по умолчанию "= 0" позволяет программе создавать объект с номером счёта по умолчанию, равным нулю.

    Checking c1 = new Checking( 124 ) ;

    Checking с2 = new Checking( ) ;

_________________

354 стр. Часть 6. Великолепная десятка

В данном случае объект c1 класса Checking создаётся с номером счёта, равным 123, тогда как объект с2 создаётся с номером счёта по умолчанию, который равен нулю.

Функции accountNo( ) и acntBalance( ) предоставляют внешнему миру доступ к защищённым членам accountNumber и balance. Задачей этих функций является предоставление внешним функциям — не членам значений, изменить которые невозможно. Кроме того, эти функции, обеспечивающие доступ к членам, предохраняют внешние функции от необходимости внесения изменений при переменах в методе хранения номера счёта или баланса.

Функции deposit( ) и withdrawal( ) отвечают за вложение и снятие денег со счёта. Поскольку функция deposit( ) довольно проста, она была определена как inline-функция. Функция withdrawal( ), будучи несколько сложнее, объявлена в классе, но определяется позже.

Функция display( ) выводит важные данные на устройство стандартного вывода.

Класс Savings, в сущности, идентичен классу Checking, за исключением дополнительного члена noWithdrawals, который отслеживает количество проведённых снятий.

Место под объекты сберегательного и чекового счёта выделяется в массивах svgAcnts и chkAcnts соответственно. Максимальное количество счетов определено величиной maxAccounts.

Функция main( ) несколько сложнее своей сестры из программы BUDGET1, поскольку она имеет дело с двумя разными типами счетов. После проверки ввода на равенство "X" функция main( ) использует конструкцию switch, чтобы выбрать тип счёта: С для чекового и S для сберегательного. Конструкция switch использована в этой программе по двум причинам: во-первых, её проще расширить, добавляя к ней дополнительные варианты; во-вторых, она предоставляет вариант default ( вариант по умолчанию ) для обработки неверного ввода.

Как и ранее, вторая часть функции main( ) обеспечивает отображение информации о счёте, собранной в первой части этой функции.

Обратите внимание на то, как содержимое классов Checking и Savings скрыто от main( ). Так, например, main( ) просит объект показать своё содержимое, однако при этом не имеет никакого представления о том, как класс выбирает, что именно и как это показать.

Функция process( ), которая обрабатывает текущие вложения и снятия, полагается на функции-члены deposit( ) и withdrawal( ), которые выполняют за неё всю чёрную работу. Хотя вы и знаете, как именно выполняются эти действия, помните, что process(  ) об этом не имеет никакого понятия. Работа счёта касается только самого класса счёта.

Советую пройти эту программу в пошаговом режиме. Ничто иное не даст более полного представления о программе, чем её рассмотрение в действии.

Хотите — верьте, хотите — нет, но с позиции программирования BUDGET2 разрабатывается легче, чем BUDGET1. Когда я писал класс Savings, я не должен был волноваться о том, как он будет использоваться главной программой ( то же относится и к классу Checking ). Когда же я работал над функцией main( ), то не думал о содержимом класса.

Однако в этой программе есть один небольшой недостаток: классы Savings и Checking имеют очень много общего, и хотелось бы найти возможность уменьшить количество повторений кода. Эта возможность реализована в очередной версии нашей программы — BUDGET3.