Нужна помощь в исправлении ошибки.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Вобщем изучаю С++ (относительно с нуля) по У.Савичу. В конце каждой главы есть практические задания. Собственно на одном из таких заданий застопорился.
Задание звучит так:
Определите класс Month как абстрактный тип данных для информации о месяце. Он должен содержать одну переменную-член типа int, представляющую месяц (1 для янв, 2 для фев и т.д.). В его состав должны входить такие элементы: конструктор, устанавливающий месяц по первым
трем буквам его названия, которые заданы в трех аргументах; конструктор, устанавливающий месяц по его целочисленному номеру, который задан в аргументе; используемый по умолчанию конструктор; функция-член input, считывающая порядковый номер месяца, и функция-член input, считывающая первые три буквы названия месяца; функция-член output, выводящая порядковый номер месяца, и функция-член output, выводящая первые три буквы названия месяца; функция-член,
возвращающая следующий месяц как значение типа Month. Функции-члены input и output имеют по одному формальному параметру типа потока. Включите определение этого класса в тестовую программу
Немного удивило требование к двум функциям-членам input и output, но суть не в этом.
С этой задачей я справился.

Далее идет продолжение программы:
Переопределите реализацию класса Month, описанного в предыдущем проекте. На этот раз месяц должен быть реализован в виде трех переменных-членов типа char, в которых хранятся первые три буквы названия месяца. Включите определение данного класса в тестовую программу.
Вобщем переделал класс. И когда я передаю конструктором числовое значение месяца для преобразования его в первые три буквы месяца, у меня срабатывает проверка на корректность ввода ((num_mon < 1) || (num_mon > 12)), хотя конструктор передает верное число, и в итоге картина Репина "Приплыли".

Код класса (интерфейс + реализация):

Код: Выделить всё

//Класс для информации о месяце
class Month
{
    public:
    Month(char let1, char let2, char let3);
    //Инициализирует значение месяца по первым 3 буквам его названия

    Month(int num_mon);
    //Инициализирует значение месяца равным num_mon
   
    Month();
    //Инициализирует значение месяца равным Jan

        void input(istream& ins = cin);
        
        void output(ostream& outs = cout);
       
        int get_month();
        //Возвращает порядковый номер месяца по первым 3 буквам
        //(Jan - 1, Feb - 2...)

        Month next_month();
        //Возвращает следующий номер месяца.

    private:
        char mon1;
        char mon2;
        char mon3;
};
=======
Month::Month(char let1, char let2, char let3): mon1(let1), mon2(let2), mon3(let3)
{
}

//ВОТ ЭТОТ КОНСТРУКТОР ДАЕТ СБОЙ
Month::Month(int num_mon)
{
    if((num_mon < 1) || (num_mon > 12))
    {
        cout << "Error.\n";
        //exit(1);
    }
    else

    if(num_mon == 1)
    {
        mon1 = 'J';
        mon2 = 'a';
        mon3 = 'n';
    }
    if(num_mon == 2)
    {
        mon1 = 'F';
        mon2 = 'e';
        mon3 = 'b';
    }
      
    ...

    if(num_mon == 12)
    {
        mon1 = 'D';
        mon2 = 'e';
        mon3 = 'c';
    }
}

Month::Month():mon1('J'), mon2('a'), mon3('n')
{
}

int Month::get_month()
{
    if((mon1 == 'J') && (mon2 == 'a') && (mon3 == 'n'))
    {
        return 1;
    }
    if((mon1 == 'F') && (mon2 == 'e') && (mon3 == 'b'))
    {
        return 2;
    }
   
    ...

    if((mon1 == 'D') && (mon2 == 'e') && (mon3 == 'c'))
    {
        return 12;
    }
}

Month Month::next_month()
{
    if(get_month() == 1)
    {
        mon1 = 'F';
        mon2 = 'e';
        mon3 = 'b';
        return (mon1 + mon2 + mon3);
    }
    if(get_month() == 2)
    {
        mon1 = 'M';
        mon2 = 'a';
        mon3 = 'r';
        return (mon1 + mon2 + mon3);
    }
    
    ...

    if(get_month() == 12)
    {
        mon1 = 'J';
        mon2 = 'a';
        mon3 = 'n';
        return (mon1 + mon2 + mon3);
    }
}

//Используем библиотеку классов isostream
void Month::input(istream& ins)
{
    cout << "What is the month now (enter first 3 letters): ";
    ins >> mon1 >> mon2 >> mon3;
}

void Month: :o utput(ostream& outs)
{
    cout << "Month is: " << mon1 << mon2 << mon3 << endl;
}
Тестирую так:

Код: Выделить всё

int main()
{
    Month mont1, mont2('A', 'u', 'g'), mont3(11);
    mont1.output();
    mont2.output();
    mont2.next_month();
    mont2.output();
    mont3.output();
    return 0;
}
Ожидаемый вывод:
Month is: Jan
Month is: Aug
Month is: Sep
Month is: Nov
В реальности:
Month is: Jan
Month is: Aug
Error.
Month is: Sep
Month is: Nov
Заранее спасибо за помощь.

P.S> Прошу камнями не бросаться, учусь. Массивы, указатели еще только предстоит изучить (через пару глав), следовательно задача решаема без них (это на всякий случай, если будут вопросы).
Аватара пользователя
Airhand
Сообщения: 239
Зарегистрирован: 06 окт 2005, 16:21
Откуда: Dnepropetrovsk

Тот конструктор, что даёт сбой: там после else должна идти открывающая скобка ( "{" ), иначе ты выпоняешь только первый if.

А если ни одно условие в get_manth не подойдёт, тогда что ?
Оптимизация по скорости:
#define while if
Оптимизация по размеру:
#define struct union
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Ты неверно понял задание. Функция next_month не должна менять текущий объект, она должна возвращать следующий месяц. По этой причине, я бы даже рекомендовал сделать эту функцию константной.

Код: Выделить всё

Month Month::next_month() const
{
    int month = get_month() + 1;
    if(13 == month)
    {
        // Для декабря следующий январь
        month = 1;
    }
    return month;
}
Тогда тестирующая функция должна будет выглядеть так:

Код: Выделить всё

int main()
{
    Month mont1, mont2('A', 'u', 'g'), mont3(11);
    mont1.output();
    mont2.output();
    mont2 = mont2.next_month();
    mont2.output();
    mont3.output();
    return 0;
}
&quot писал(а):Тот конструктор, что даёт сбой: там после else должна идти открывающая скобка ( "{" ), иначе ты выпоняешь только первый if.
Да, это тоже проблема, только описано не совсем понятно. Airhand, имел ввиду, что в else попадает только первый if. Остальные if будут выполняться в любом случае, даже если произошла ошибка. Исправить можно либо взяв все if, начиная с if(num_mon == 1) и до конца в фигурные скобки, либо дописав перед каждым if слово else, чтобы мне больше нравится.

Это проблема влияет только на работу программы в случае ошибки, но при верных данных там всё будет хорошо. Главной проблемой является имплементация функции next_month.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Вобщем нашел еще в чем проблема. Нашел логическую ошибку.
next_month возвращал сумму 3 переменных типа char, тем самым конструктор думал, что это int и логично, что проверка не проходила успешно.

Решение оказалось до банальности простым. Создал мутатор:

Код: Выделить всё

void set_month(char month1, char month2, char month3);
//Постусловие: устанавливает значения первых 3 буквы месяца,
//по 1 букве в каждый аргумент.


void Month::set_month(char month1, char month2, char month3)
{
    mon1 = month1;
    mon2 = month2;
    mon3 = month3;
}
И немного подкорректировал ф-цию next_month.
(пока оставил, как было т.е. меняется значение переменной - буду править)

Код: Выделить всё

Month Month::next_month()
{
    Month temp;
    if(get_month() == 1)
    {
        temp.set_month('F', 'e', 'b');
        return temp;
    }
    if(get_month() == 2)
    {
        temp.set_month('M', 'a', 'r');
        return temp;
    }

    ...

    if(get_month() == 12)
    {
        temp.set_month('J', 'a', 'n');
        return temp;
    }
}
Ну а в тесте изменилась строка (спасибо Romeo):

Код: Выделить всё

mont2 = mont2.next_month();
Теперь озадачился с проверкой, как это лучше сделать: если будет введено не Aug, а Avg?!
Как вариант, создать закрытую функцию, которая будет проверять комбинации 3 переменных (т.е. 12 проверок). Если нету верной комбинации, то прекращать выполнение программы. Но смущает итоговый размер класса - огромный просто (по моим меркам).

P.S> да, практиковать и практиковать, малое кол-во опыта сказывается и посему чувствуется скованость рук и мыслей... у меня разумеется.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

А чем тебе мой next_month не понравился? Он красивше значительнее и позволяет не писать огромный if из 12 веток. Вообще ты с этим здоровым if перебрал, они у тебя почти в каждой функции :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Romeo писал(а):А чем тебе мой next_month не понравился? Он красивше значительнее и позволяет не писать огромный if из 12 веток. Вообще ты с этим здоровым if перебрал, они у тебя почти в каждой функции :)
Да, с if я пока перебарщиваю (навел справки, в серьезных проектах классы в среднем 80-100 строк занимают, хотя знаю - строки не показатель, показатель - результат), нужно как-то более удобные методы проверок осуществлять.
А вообще твой вариант мне нравится (оптимальный вариант :) ), но что-то мне кажется, что в измененной версии надо возвращать 3-буквенное значение.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

&quot писал(а):но что-то мне кажется, что в измененной версии надо возвращать 3-буквенное значение.
Зачем? В моём варианте автоматически вызывается конструктор Month(int), который и проставляет три буквы по номеру месяца. Это наиболее правильный подход, так как если будешь проставлять три буквы отдельно, то снова скопируешь громадный if.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Romeo писал(а):Зачем? В моём варианте автоматически вызывается конструктор Month(int), который и проставляет три буквы по номеру месяца. Это наиболее правильный подход, так как если будешь проставлять три буквы отдельно, то снова скопируешь громадный if.
Да, ваш вариант намного лучше.
Опыта видно не хватает. Т.к. сейчас пытаюсь понять как все работает - как именно вызывается конструктор и проставляются буквы, вместо цифры.

Допустим есть переменная month1 Неважно как мы инициализируем ее значение, в любом случае переменные-члены будут равны первым 3 буквам месяца (пусть это будет Oct).
Дальше вызываем month1.next_month().
Внутри функции мы создаем целочисленную переменную, которой присваивается результат вызова функции get_month() + 1. Функция соответственно вызывается для переменной month1 и ей передаются значения ее переменных-членов т.е. Oct. Значит переменная month = 11, что собственно и возвращает функция next_month. Но функция должна возвращать у нас тип Month (т.е. 3 char значения, а получает 1 int). И вот тут не могу понять, как она преобразовывает этот int в 3 char'а :(
Только за счет того, что мы в main это значение присваиваем переменной и это и является вызовом конструктора???
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Если переменной типа Month при инициализации присвоить значение типа int, то будет вызван конструктор Month(int). То же самое произойдёт, если функция возвращает значение типа Month, а в return мы пишем значение типа int.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить