Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C/C++ - [решено] Помогите дорешать задачу с созданием класса Book

Ответить
Настройки темы
C/C++ - [решено] Помогите дорешать задачу с созданием класса Book

Новый участник


Сообщения: 14
Благодарности: 0

Профиль | Отправить PM | Цитировать


Изменения
Автор: Drongo
Дата: 23-11-2008
Описание: Используем тег [code]Ваш код[/code]
Здравствуйте помогите пожалуйста довести задачу до ума

Вот задание:Создать класс типа — книга. Поля — название, автор, год выпуска, вид литературы (худож., методич., справочн., ...). Класс имеет конструктор, деструктор. Методы: установки и возвращения значения полей, вычисления «возраста» книги, метод печати. Методы установки полей класса должны проверять корректность задаваемых параметров. Проверить работу этого класса.

Вот исходник:
Код: Выделить весь код
#include "stdafx.h" 
#include "stdio.h" 
#include "conio.h" 
#include "math.h" 
#define _CRT_NONSTDC_NO_DEPRECATE 
class book{ 
    char* avtor,*nazv,*vid; 
    int year; 
public: book (int y, char* v, char* a,char* n){ 
            avtor=a; 
            year=y; 
            vid=v; 
            nazv=n; 
    } 
        { 
        char *avtor = new char[strlen(a)+1];    //Выделяем память 
        strcpy(avtor.a); 
        char *vid = new char[strlen(v)+1]; 
        strcpy(vid.v); 
        char *nazv = new char[strlen(n)+1]; 
        strcpy(nazv.n); 
        } 
        char *get_avtor(){return avtor;} 
        char *get_vid(){return vid;} 
        int get_year(){return year;} 
        char *get_nazv(){return nazv;} 
 
 
~book(){delete [avtor,vid,nazv] book;}  //деструктор 
 
 
};     
void main() 
{ 
    return ; 
}

Отправлено: 04:11, 23-11-2008

 

Аватара для Drongo

Будем жить, Маэстро...


Moderator


Сообщения: 6695
Благодарности: 1391

Профиль | Сайт | Отправить PM | Цитировать


В общем немного решил, но я сам запутался, знаний нехватает. Самому интересно.
У меня вопрос:
1. Как организовать ввод данных с клавиатуры?
2. И как правильно записать деструктор? Я запутался...

По первому вопросу придумал так:
В определении класса объявляю функцию
Код: Выделить весь код
void UserFromKeyboard();
В реализации этой функции считываю через дополнительные переменные, данные, и передаю в функции установки "set", каждое значение в свою функцию, но выпадает ошибка о том что приложение будет закрыто. Причём если только одно значение считывается и передаётся, то программа работает, а если четыре, то никак. Ошибки выскакивают при вводе символьных значений задачи. Подскажите как правильно ввести с клавиатуры значения и сохранить\присвоить полям объекта?
Решение
Код: Выделить весь код
// Создать класс типа — книга.
// Поля — название, автор, год выпуска, вид литературы (худож., методич., справочн., ...).
// Класс имеет конструктор, деструктор.
// Методы: установки и возвращения значения полей, вычисления «возраста» книги,
// Метод печати.
// Методы установки полей класса должны проверять корректность задаваемых параметров.
// Проверить работу этого класса

//#include "stdafx.h"
#include <iostream.h>
using std::cout;
using std::cin;
using std::endl;

#include <cstring.h>
#include <cassert>
//#include "math.h"
//#include <stdarg.h>
#include <stdio.h>
#include "conio.h"
                 
//#define _CRT_NONSTDC_NO_DEPRECATE 
class Book{
public:
  Book(char *au, char *nb, char *jr, int yr); // Конструктор
//  void UserFromKeyboard();
  //void setBook(char *au, char *nb, char *jr, int yr);
  // Функции "set", установки значений полей книги
  void setAuthorBook(char *au); // Установка имени автора книги
  void setNameBook(char *nb);   // Установка названия книги
  void setJanreBook(char *jr);  // Установка жанра книги
  void setYearBook(int yr);   // Установка года выпуска книги

  // Функции "get", возвращение значений полей книги
  char *getAuthorBook() { return author; } ;// Возвращение имени автора книги
  char *getNameBook()  { return nameBook; };   // Возвращение названия книги
  char *getJanreBook()  { return janr; };  // Возвращение жанра книги
  int getYearBook()   { return year; };// Возвращение года выпуска книги

  void printBook();  // Печать значений книги
  void CalculateYearBook();
  ~Book(); // Деструктор
private:
    char *author;
    char *nameBook;
    char *janr;
    int year;
};
// Конструктор----------------------------------------------------------------
Book::Book(char *au, char *nb, char *jr, int yr)
{
   setAuthorBook(au);
   setNameBook(nb);
   setJanreBook(jr);
   setYearBook(yr);
}
// Функция ввода данных значений книги с клавиатуры
//void Book::UserFromKeyboard()
//{

//}
// Функция установки Автора Книги
void Book::setAuthorBook(char *au)
{
  // Проверка и выделение памяти для поля - Автор
  int len = strlen(au);
  if( 0 <= len && len < 256){ // Проверка диапазона, должен быть меньше 255
    author = new char[strlen(au) + 1]; // Выделение памяти в 255 байт плюс завершающий символ
    assert(author != 0);  // Проверка выделения памяти
    strcpy(author, au);  // Копирование...
   }
  else   // Вывод ошибки если диапазон массива меньше нуля и больше 256 байт...
    cout<<" Error! Lenght > 256 bytes!";
}
// Функция установки Названия книги
void Book::setNameBook(char *nb)
{
  // Проверка и выделение памяти для поля - Имя Книги
  int len = strlen(nb);
  if(len >= 0 && len < 256){ // Проверка диапазона, должен быть меньше 255
    nameBook = new char[strlen(nb) + 1]; // Выделение памяти в 255 байт плюс завершающий символ
    assert(nameBook != 0);  // Проверка выделения памяти
    strcpy(nameBook, nb);  // Копирование...
   }
  else   // Вывод ошибки если диапазон массива меньше нуля и больше 256 байт...
    cout<<" Error! Lenght > 256 bytes!";
}
// Функция установки Жанра Книги
void Book::setJanreBook(char *jr)
{
  // Проверка и выделение памяти для поля - Жанр
  int len = strlen(jr);
  if(len >= 0 && len < 256){ // Проверка диапазона, должен быть меньше 255
    janr = new char[strlen(jr) + 1]; // Выделение памяти в 255 байт плюс завершающий символ
    assert(janr != 0);  // Проверка выделения памяти
    strcpy(janr, jr);  // Копирование...
   }
  else   // Вывод ошибки если диапазон массива меньше нуля и больше 256 байт...
    cout<<" Error! Lenght > 256 bytes!";
}
// Функция установки Года Книги
void Book::setYearBook(int yr)
{
  // Проверка значений поля - Год Книги
  if(yr < 2008) // Проверка диапазона, должен быть меньше 255
    year = yr;
  else   // Вывод ошибки если диапазон массива меньше нуля и больше 256 байт...
    cout<<" Error! Year > 2008 !";
}
// Расчёт возраста Книги
void Book::CalculateYearBook()
{
   int MyYear = 2008,  // Текущий год
       resultYear;  // Результат расчёта возраста книги

   resultYear = MyYear - year;

   cout<<"\n This Book "<<resultYear<<" Years! \n"<<endl;
}
// Печать объекта-------------------------------------------------------------
void Book::printBook()
{
   cout<<" Author Book: "<<author<<endl;
   cout<<" Name Book: "<<nameBook<<endl;
   cout<<" Janre Book: "<<janr<<endl;
   cout<<" Year Book: "<<year<<endl;
}
// Деструктор-----------------------------------------------------------------
Book::~Book()
{
}
//---------------------------------------------------------------------------
void main()
{
   int z;
   Book b("Servantes", "Don Kixot", "Romantic", 1550);

   b.printBook();
   b.CalculateYearBook();

   b.setAuthorBook("Pushkin");
   b.setNameBook("Eugeny Onegin");
   b.setJanreBook("Roman");
   b.setYearBook(1834);
   b.printBook();
   b.CalculateYearBook();

   cin>>z;
} 
//---------------------------------------------------------------------------

-------
Правильная постановка вопроса свидетельствует о некотором знакомстве с делом.
3нание бывает двух видов. Мы сами знаем предмет — или же знаем, где найти о нём сведения.
[Quick Killer 3.0 Final [OSZone.net]] | [Quick Killer 3.0 Final [SafeZone.cc]] | [Парсер логов Gmer] | [Парсер логов AVZ]


Отправлено: 15:15, 23-11-2008 | #2



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

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

pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Деструктор:
Код: Выделить весь код
Book::~Book()
{
  delete [] author;
  delete [] nameBook;
  delete [] janr;
}
Вопрос меня смущает: ежели используешь стандартную библиотеку, почему бы не использовать стандартные контейнеры? Например класс string легко превращается в char* при необходимости. С памятью они очень нежно обращаются.
Код: Выделить весь код
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

class Book
{
public:
    Book(const string& au, const string& nb, const string& jr, unsigned int yr);
    Book(istream& input);// для ввода из потока

    void setAuthorBook(const string& au) {author = checkLength(au);}
    void setNameBook(const string& nb)   {nameBook = checkLength(nb);}
    void setJanreBook(const string& jr)  {janr = checkLength(jr);}
    void setYearBook(int yr)             {year = checkYear(jr);}

    const string& getAuthorBook() { return author; }
    const string& getNameBook()   { return nameBook; }
    const string& getJanreBook()  { return janr; }
    unsigned int  getYearBook()   { return year; }

    void printBook();
    void calculateYearBook();

private:

    string author;
    string nameBook;
    string janr;
    unsigned int year;

    static void checkLength(const string& str);
    static const unsigned& checkYear(const unsigned&);
};

const Book::string& Book::checkLength(const string& str)
{
    if (str.empty() || 255<=str.size())
    {
        throw out_of_range(
                "Book::checkLength "
                    "a string which is empty or greater 255 chars is not permitted");
    }

    return str;
}

const unsigned& Book::checkYear(const unsigned& yr)
{
    if (2008 <= yr)
    {
        throw out_of_range(
                "Book::checkYear "
                    "year>=2008 is not permitted");
    }

    return str;
}

Book::Book(const string& au, const string& nb, const string& jr, unsigned int yr) :
    author  (checkLength(au)),
    nameBook(checkLength(nb)),
    janr    (checkLength(jr)),
    year    (yr)
{
}

Book::Book(std::stream& input) :
    author  (),
    nameBook(),
    janr    (),
    year    ()
{
    // использование копирования позволяет сохранить объект в целости и сохранности
    // в случае исключения. Например если при чтении любого поля возникнет ошибка,
    // то следующий код оставит книгу b1 без изменений:

    // Book b1(...);
    // b1.setAuthorBook(...);
    // ...
    // try
    // {
    //   b1 = Book(cin); // произошла ошибка при чтении года!!!
    // }
    // catch(runtime_error& err)
    // {
    //    clog << "error: " << err.what() << endl;
    // }

    clog << "reading data from stream\n";

    if (((clog << "autor [enter]: "),       getline(input, author)) &&
        ((clog << "nameBook [enter]: "),    getline(input, nameBook)) &&
        ((clog << "janr [enter]: "),        getline(input, janr)) &&
        ((clog << "year [any space character]: "), (input >> jear)))
    {
        clog << "checking data\n";

        checkLength(author);
        checkLength(nameBook);
        checkLength(janr);
        checkYear(yr);

        clog << "all ok\n";
    }
    else
    {
         throw runtime_error("error reading from stream");
    }
}

// Печать объекта-------------------------------------------------------------
void Book::printBook()
{
   cout<<" Author Book: "<<author<<endl;
   cout<<" Name Book: "<<nameBook<<endl;
   cout<<" Janre Book: "<<janr<<endl;
   cout<<" Year Book: "<<year<<endl;
}
//---------------------------------------------------------------------------
void main()
{
   Book b("Servantes", "Don Kixot", "Romantic", 1550);

   b.printBook();
   b.CalculateYearBook();

   b.setAuthorBook("Pushkin");
   b.setNameBook("Eugeny Onegin");
   b.setJanreBook("Roman");
   b.setYearBook(1834);
   b.printBook();
   b.CalculateYearBook();

    { // вариант 1
        Book book_from_stream(cin);
        book_from_stream.printBook();
    }

    { // вариант 2
        b = Book(cin);
        b.printBook();
    }
}

Последний раз редактировалось pva, 23-11-2008 в 17:11. Причина: неправильно обработал ошибки потока

Это сообщение посчитали полезным следующие участники:

Отправлено: 17:04, 23-11-2008 | #3


Аватара для Drongo

Будем жить, Маэстро...


Moderator


Сообщения: 6695
Благодарности: 1391

Профиль | Сайт | Отправить PM | Цитировать


pva, Спасибо за пояснения!!!
Цитата pva:
Вопрос меня смущает: ежели используешь стандартную библиотеку, почему бы не использовать стандартные контейнеры? »
Это потому, что опыта нехватает.

pva, У меня при компиляции вашего варианта, выскакивает несколько ошибок... Что я не так делаю?

-------
Правильная постановка вопроса свидетельствует о некотором знакомстве с делом.
3нание бывает двух видов. Мы сами знаем предмет — или же знаем, где найти о нём сведения.
[Quick Killer 3.0 Final [OSZone.net]] | [Quick Killer 3.0 Final [SafeZone.cc]] | [Парсер логов Gmer] | [Парсер логов AVZ]


Отправлено: 17:31, 23-11-2008 | #4

pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Цитата Drongo:
У меня при компиляции вашего варианта, выскакивает несколько ошибок... Что я не так делаю? »
э.. это сорри, это я не так делаю. Я сначала сделал без using namespace std, а вместо этого внутри Book вставил typedef std::string string. А переделать Book::string в string забыл Если переделать, то ошибки пропадут Сейчас напишу самый "доведённый до ума" вариант:
Код: Выделить весь код
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

/*-----------------23.11.2008 22:01-----------------
 * Класс Book - доступ к видимым полям, ввод-вывод
 * --------------------------------------------------*/

class Book
{
public:
    Book(const string& author1,
            const string& title1,
            const string& genre1,
            unsigned int year1);

    Book(); // для стандартных контейнеров книг

    Book(istream& input);// для ввода из потока

    // установка свойств
    void setAuthor(const string& author1) {_author = _checkLength(author1);}
    void setTitle(const string& title1)   {_title  = _checkLength(title1); }
    void setGenre(const string& genre1)   {_genre  = _checkLength(genre1); }
    void setYear(unsigned int year1)      {_year   = _checkYear  (year1);  }

    // чтение свойств
    const string& author() const throw() { return _author; }
    const string& title()  const throw() { return _title; }
    const string& genre()  const throw() { return _genre; }
    unsigned int  year()   const throw() { return _year; }

    // ввод-вывод
    friend ostream& operator<<(ostream&, const Book&);
    friend istream& operator>>(istream&, Book&);

    // разделитель полей для ввода-вывода
    static const char _io_field_delimiter;

    // ввод-вывод для пользователя.
    void human_read_unsafe();
    void human_write();

private:

    string       _author;
    string       _title;
    string       _genre;
    unsigned int _year;

    static const string& _checkLength(const string& str);
    static const unsigned& _checkYear(const unsigned&);
};

/*-----------------23.11.2008 22:01-----------------
 * Функции ввода-вывода не роботу
 * --------------------------------------------------*/

void Book::human_write()
{
    // просто в красивом виде
    cout << "Author:." << _author << "\n"
         << "Title:.." << _title  << "\n"
         << "Genre:.." << _genre  << "\n"
         << "Year:..." << _year   << "\n\n";
}

void Book::human_read_unsafe()
{
    // читаем, проверяем на ошибки, но пишем сразу в книгу,
    // то есть портим сам объект, поэтому unsafe.
    // А вот если потом скопировать результат в хранилище,
    // получается вполне безопасно

    cout << "Type in book properties:\n";

    // ws(istream&) - пропускает пробелы (enter тоже) между словами
    // getline(istream&, string&, int symbol='\n') - читает строчку до появления символа symbol

    cout << "Author:.";
    getline(ws(cin), _author, '\n');
    _checkLength(_author);

    cout << "Title:..";
    getline(ws(cin), _title,  '\n');
    _checkLength(_title );

    cout << "Genre:..";
    getline(ws(cin), _genre,  '\n');
    _checkLength(_genre );

    cout << "Year:...";
    cin >> _year;
    _checkYear  (_year  );

    cout << "thank you!\n\n";
}

/*-----------------23.11.2008 22:02-----------------
 * и как это всё делается.
 * --------------------------------------------------*/

// прикольно выглядит, если поставить = '|'
//const char Book::_io_field_delimiter = '|';
const char Book::_io_field_delimiter = '\t';

Book::Book() :
    _author ("Unknown Author"),
    _title  ("Unknown Author"),
    _genre  ("Unknown Genre"),
    _year   ()
{
    // не хочется, но надо...
}

Book::Book(const string& author1, const string& title1,
        const string& genre1, unsigned int year1) :
    _author (_checkLength(author1)),
    _title  (_checkLength(title1 )),
    _genre  (_checkLength(genre1 )),
    _year   (_checkYear  (year1  ))
{
    // один из подходов последовательной проверки при построении класса.
    // неправильного класса не получится.
}

Book::Book(istream& in) :
    _author (),
    _title  (),
    _genre  (),
    _year   ()
{
    // использование копирования позволяет сохранить объект в целости и сохранности
    // в случае исключения. Например если при чтении любого поля возникнет ошибка,
    // то следующий код оставит книгу b1 без изменений:

    // Book b1(...);
    // b1.setAuthorBook(...);
    // ...
    // try
    // {
    //   b1 = Book(cin); // произошла ошибка при чтении года!!!
    // }
    // catch(runtime_error& err)
    // {
    //    clog << "error: " << err.what() << endl;
    // }

    if (getline(ws(in), _author, _io_field_delimiter) &&
	    getline(ws(in), _title,  _io_field_delimiter) &&
	    getline(ws(in), _genre,  _io_field_delimiter) &&
	    (in >> _year))
    {
    	// проверяем только в случае, если считалось полностью.
    	// если не полностью (поток кончился), то in.good(),
    	// он же `operator istream::bool()` выдаст false
	    _checkLength(_author);
    	_checkLength(_title );
	    _checkLength(_genre );
	    _checkYear  (_year  );
    }
    
    // чтобы отловить случаи неполного чтения, нужно установить флаг:
    // in.exceptions(ios::bad_bit|ios::fail_bit);
}


const string& Book::_checkLength(const string& str)
{
    // вынесли отдельно, чтоб съэкономить код

    if (str.empty() || 255<=str.size())
    {
        throw out_of_range(
            "Book::checkLength "
                "a string which is empty or greater 255 chars is not permitted");
    }

    return str;
}

const unsigned& Book::_checkYear(const unsigned& year)
{
    if (2008 <= year)
    {
        throw out_of_range(
                "Book::checkYear "
                    "year>=2008 is not permitted");
    }

    return year;
}

ostream& operator<<(ostream& out, const Book& book)
{
    // вывод в таблицу, разделённую табуляторами и переводом строки
    // после вывод буфер не сбрасываем.

    return
        out << book._author << Book::_io_field_delimiter
        << book._title  << Book::_io_field_delimiter
        << book._genre  << Book::_io_field_delimiter
        << book._year   << "\n";
}

istream& operator>>(istream& in, Book& book)
{
    // стараемся не запортить book если произойдёт исключение
    book = Book(in);
    return in;
}

/*-----------------23.11.2008 22:14-----------------
 * Демонстрация возможностей
 * --------------------------------------------------*/

#include <fstream>
#include <list>
#include <map>


void main()
{
    // прикольный способ создавать функции в теле функции (как в паскале) - если кто не знал ;-)
    // но, к сожалению, длинный код может запутать, пожтому при возможности лучше выносить наружу.

    class Job
    {
    private:
        string      _cmd;
        list<Book>  _books;
        // да, в C++ писанины много...
        // void (Job::*)() - так выглядит тип "указатель на функцию void Job::function()"
        map<string,pair<void (Job::*)(), const char*> > _handlers;

        bool enter_cmd()
        {
            cout << "Enter what to do (\"exit\" to quit or \"help\" for more info): ";
            _cmd.clear();
            return (cin >> _cmd) && _cmd!="exit";
        }

    public:
        Job()
        {
            _handlers["help"  ] = make_pair(cmd_help,  "brief about instructions");
            _handlers["add"   ] = make_pair(cmd_add,   "add book to storage");
            _handlers["clear" ] = make_pair(cmd_clear, "clear storage");
            _handlers["save"  ] = make_pair(cmd_save,  "save storage to file \"storage.txt\"");
            _handlers["load"  ] = make_pair(cmd_load,  "load storage from file \"storage.txt\"");
            _handlers["erase" ] = make_pair(cmd_erase, "erase one book from storage");
            _handlers["type"  ] = make_pair(cmd_type,  "display storage content");
        }

        // так класс превращается в функциональный объект.

        // Job job1;
        // ...
        // job1();

        void operator()()
        {
            // полезный приём для избежания break внутри цикла. Облегчает чтение кода
            // описание функции в теле класса даёт указание раскрывать её как inline
            while (enter_cmd())
            {
                map<string,pair<void (Job::*)(), const char*> >::iterator iter = _handlers.find(_cmd);

                // оператор map<...>::operator[] добавляет элемент, если он не найден в списке,
                // а нам этого совсем не надо. Поэтому используем  поиск по ключу
                if (iter!=_handlers.end())
                {
                    try
                    {
                        // програмное исключение exception не приводит к неработоспособности этого кода,
                        // поэтому останавливаем их обработку здесь с чистым сердцем.
                        // часто люди злоупотребляют блоками try..catch и оставляют работать
                        // повреждённую программу.

                        (this->*iter->second.first)();
                    }
                    catch(exception& e)
                    {
                        clog << "runtime error: " << e.what() << endl;
                    }
                }
                else
                {
                    cout << "Unexpected command! Type \"help\" for list of avaible commands\n";
                }
            }

            cout << "Bye!";
        }

        // далее пошли наши команды

        void cmd_help()
        {
            // прикольно выглядит:
    //        cout.fill('.');
            cout.flags(ios::left);

            // map<> держит ключи в сортированном виде, поэтому вывод будет в
            // алфавитном порядке

            for (map<string,pair<void (Job::*)(), const char*> >::iterator
                    first=_handlers.begin(), last=_handlers.end(); first!=last; ++first)
            {
                cout.width(10);
                cout << first->first << first->second.second << "\n";
            }
        }

        void cmd_type()
        {
            // вот так просто теперь выглядит вывод
            // ostream_iterator<> использует оператор << для вывода в поток
            copy(_books.begin(), _books.end(), ostream_iterator<Book,char>(cout));
        }

        void cmd_add()
        {
            // небезопасно читаем новую книгу
            // и безопасно добавляем в контейнер
            Book new_book;
            new_book.human_read_unsafe();
            _books.push_back(new_book);
        }

        void cmd_clear()
        {
            _books.clear();
        }

        void cmd_save()
        {
            // вот так просто теперь выглядит сохранение
            ofstream out("storage.txt");
            copy(_books.begin(), _books.end(), ostream_iterator<Book,char>(out));
        }

        void cmd_load()
        {
            // загружаем в отдельный список - безопасно при исключениях
            list<Book> new_list;
            ifstream in("storage.txt");
            copy(istream_iterator<Book,char>(in), istream_iterator<Book,char>(), back_inserter(new_list));

            // меняем местами содержимое списков. При чистке стека старый список удалится.
            new_list.swap(_books);
        }

        void cmd_erase()
        {
            // пример использования функционального класса
            // title_predicate(book) возвращает true если введённое название книги совпадает
            // иначе false

            struct title_predicate
            {
                string _title1;

                title_predicate()
                {
                    cout << "Enter title to erase: ";
                    cin >> _title1;
                }

                bool operator()(const Book& book)
                {
                    return book.title()==_title1;
                }
            };

            // remove_if перемещает в конец списка все элементы, которые удовлетворяют условию.
            // если до выполнения erase произойдёт исключение, книги не потеряются. Но правда
            // изменится порядок.
            _books.remove_if(/*это вызов конструктора*/ title_predicate());
        }
    };


    // вот теперь настал тот момент, когда непонятно, что кончилось и что началось,
    // если помещать описание класса внутрь функции.
    Job()();
}
проверил - работает

Последний раз редактировалось pva, 23-11-2008 в 22:44. Причина: у списка есть оптимизированный remove_if

Это сообщение посчитали полезным следующие участники:

Отправлено: 22:30, 23-11-2008 | #5


Аватара для Drongo

Будем жить, Маэстро...


Moderator


Сообщения: 6695
Благодарности: 1391

Профиль | Сайт | Отправить PM | Цитировать


Цитата pva:
Сейчас напишу самый "доведённый до ума" вариант »
Ого, класс, Только у меня опять ошибки, 26 штук... Компилирую Borland C++ Builder 6.0.

-------
Правильная постановка вопроса свидетельствует о некотором знакомстве с делом.
3нание бывает двух видов. Мы сами знаем предмет — или же знаем, где найти о нём сведения.
[Quick Killer 3.0 Final [OSZone.net]] | [Quick Killer 3.0 Final [SafeZone.cc]] | [Парсер логов Gmer] | [Парсер логов AVZ]


Отправлено: 11:38, 24-11-2008 | #6

pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Вот это обкатал на 6-м билдере. К сожалению C++ Builder - это не компилятор C++
Код: Выделить весь код
#pragma hdrstop
//------------------------------

#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

/*-----------------23.11.2008 22:01-----------------
 * Класс Book - доступ к видимым полям, ввод-вывод
 * --------------------------------------------------*/

class Book
{
public:
    Book(const string& author1,
            const string& title1,
            const string& genre1,
            unsigned int year1);

    Book(); // для стандартных контейнеров книг

    // установка свойств
    void setAuthor(const string& author1) {_author = _checkLength(author1);}
    void setTitle(const string& title1)   {_title  = _checkLength(title1); }
    void setGenre(const string& genre1)   {_genre  = _checkLength(genre1); }
    void setYear(unsigned int year1)      {_year   = _checkYear  (year1);  }

    // чтение свойств
    const string& author() const throw() { return _author; }
    const string& title()  const throw() { return _title; }
    const string& genre()  const throw() { return _genre; }
    unsigned int  year()   const throw() { return _year; }

    // ввод-вывод
    friend ostream& operator<<(ostream&, const Book&);
    friend istream& operator>>(istream&, Book&);

    // разделитель полей для ввода-вывода
    static const char _io_field_delimiter;

    // ввод-вывод для пользователя.
    void human_read_unsafe();
    void human_write();

private:

    string       _author;
    string       _title;
    string       _genre;
    unsigned int _year;

    static const string& _checkLength(const string& str);
    static const unsigned& _checkYear(const unsigned&);
};

/*-----------------23.11.2008 22:01-----------------
 * Функции ввода-вывода не роботу
 * --------------------------------------------------*/

void Book::human_write()
{
    // просто в красивом виде
    cout << "Author:." << _author << "\n"
         << "Title:.." << _title  << "\n"
         << "Genre:.." << _genre  << "\n"
         << "Year:..." << _year   << "\n\n";
}

void Book::human_read_unsafe()
{
    // читаем, проверяем на ошибки, но пишем сразу в книгу,
    // то есть портим сам объект, поэтому unsafe.
    // А вот если потом скопировать результат в хранилище,
    // получается вполне безопасно

    cout << "Type in book properties:\n";

    // ws(istream&) - пропускает пробелы (enter тоже) между словами
    // getline(istream&, string&, int symbol='\n') - читает строчку до появления символа symbol

    cout << "Author:.";
    getline(ws(cin), _author, '\n');
    _checkLength(_author);

    cout << "Title:..";
    getline(ws(cin), _title,  '\n');
    _checkLength(_title );

    cout << "Genre:..";
    getline(ws(cin), _genre,  '\n');
    _checkLength(_genre );

    cout << "Year:...";
    cin >> _year;
    _checkYear  (_year  );

    cout << "thank you!\n\n";
}

/*-----------------23.11.2008 22:02-----------------
 * и как это всё делается.
 * --------------------------------------------------*/

// прикольно выглядит, если поставить = '|'
//const char Book::_io_field_delimiter = '|';
const char Book::_io_field_delimiter = '\t';

Book::Book() :
    _author ("Unknown Author"),
    _title  ("Unknown Title"),
    _genre  ("Unknown Genre"),
    _year   ()
{
    // не хочется, но надо...
}

Book::Book(const string& author1, const string& title1,
        const string& genre1, unsigned int year1) :
    _author (_checkLength(author1)),
    _title  (_checkLength(title1 )),
    _genre  (_checkLength(genre1 )),
    _year   (_checkYear  (year1  ))
{
    // один из подходов последовательной проверки при построении класса.
    // неправильного класса не получится.
}

const string& Book::_checkLength(const string& str)
{
    // вынесли отдельно, чтоб съэкономить код

    if (str.empty() || 255<=str.size())
    {
        throw out_of_range(
            "Book::checkLength "
                "a string which is empty or greater 255 chars is not permitted");
    }

    return str;
}

const unsigned& Book::_checkYear(const unsigned& year)
{
    if (2008 <= year)
    {
        throw out_of_range(
                "Book::checkYear "
                    "year>=2008 is not permitted");
    }

    return year;
}

ostream& operator<<(ostream& out, const Book& book)
{
    // вывод в таблицу, разделённую табуляторами и переводом строки
    // после вывод буфер не сбрасываем.

    return
        out << book._author << Book::_io_field_delimiter
        << book._title  << Book::_io_field_delimiter
        << book._genre  << Book::_io_field_delimiter
        << book._year   << "\n";
}

istream& operator>>(istream& in, Book& book)
{
    // немного подумав обрал коснтруктор из потока.
    // потому что при чтении итератором istream_iterator<Book> будет всё равно
    // использоваться копия Book, которая находятся в самом итераторе.

    // использование копирования позволяет сохранить объект в целости и сохранности
    // в случае исключения. Например если при чтении любого поля возникнет ошибка,
    // то следующий код оставит книгу b1 без изменений:

    // Book b1(...);
    // b1.setAuthorBook(...);
    // ...
    // try
    // {
    //   b1 = Book(cin); // произошла ошибка при чтении года!!!
    // }
    // catch(runtime_error& err)
    // {
    //    clog << "error: " << err.what() << endl;
    // }

    if (getline(ws(in), book._author, Book::_io_field_delimiter) &&
	    getline(ws(in), book._title,  Book::_io_field_delimiter) &&
	    getline(ws(in), book._genre,  Book::_io_field_delimiter) &&
	    (in >> book._year))
    {
    	// проверяем только в случае, если считалось полностью.
    	// если не полностью (поток кончился), то in.good(),
    	// он же `operator istream::bool()` выдаст false
	    Book::_checkLength(book._author);
    	Book::_checkLength(book._title );
	    Book::_checkLength(book._genre );
	    Book::_checkYear  (book._year  );
    }

    // чтобы отловить случаи неполного чтения, нужно установить флаг:
    // in.exceptions(ios::badbit|ios::failbit);
    // но это мешает чтению из потока до его конца (в конце выйдет исключение)
    return in;
}

/*-----------------23.11.2008 22:14-----------------
 * Демонстрация возможностей
 * --------------------------------------------------*/

#include <fstream>
#include <iterator>
#include <list>
#include <map>

// пример использования функционального класса
// title_predicate(book) возвращает true если введённое название книги совпадает
// иначе false
// используется в Job::cmd_erase(), но сюда вынесено, потому что борландовский
// компилятор умеет подставлять в шаблоны только классы, видимые снаружи
// (а не в текущем блоке). Дебильная урезка по-моему.

struct title_predicate
{
    string _title1;

    title_predicate()
    {
        cout << "Enter title to erase: ";
        getline(ws(cin), _title1);
    }

    bool operator()(const Book& book)
    {
        return book.title()==_title1;
    }
};

void main()
{
    // прикольный способ создавать функции в теле функции (как в паскале) - если кто не знал ;-)
    // но, к сожалению, длинный код может запутать, пожтому при возможности лучше выносить наружу.

    class Job
    {
    private:
        string      _cmd;
        list<Book>  _books;
        // да, в C++ писанины много...
        // void (Job::*)() - так выглядит тип "указатель на функцию void Job::function()"
        // борланд такую конструкцию не воспринимает :( ему обязательно надо typedef
        // и вообще у него из-за ввода __closure началась путаница с указателями на методы
        // я привёл к стандартному сишному вызову cdecl
        typedef void (cdecl Job::*handler_type)();
        // без этого тоже не хотел собирать почему-то.
        // а всё глюкавый сборщик шаблонов виноват...
        typedef map<string,pair<handler_type, const char*> > handler_map_type;
        handler_map_type _handlers;

        bool enter_cmd()
        {
            cout << "Enter what to do (\"exit\" to quit or \"help\" for more info): ";
            _cmd.clear();
            return (cin >> _cmd) && _cmd!="exit";
        }

    public:
        Job()
        {
            // борландовский компилятор не по правилам делает порядок подстановки типов аргументов в шаблон
            // поэтому приходится указывать тип аргумента явно.
            // Указатель на функцию тоже почему-то стал браться с амперсандом, хотя по правилам - без...
            _handlers["help"  ] = make_pair(static_cast<handler_type>(&Job::cmd_help),  "brief about instructions");
            _handlers["add"   ] = make_pair(static_cast<handler_type>(&Job::cmd_add),   "add book to storage");
            _handlers["clear" ] = make_pair(static_cast<handler_type>(&Job::cmd_clear), "clear storage");
            _handlers["save"  ] = make_pair(static_cast<handler_type>(&Job::cmd_save),  "save storage to file \"storage.txt\"");
            _handlers["load"  ] = make_pair(static_cast<handler_type>(&Job::cmd_load),  "load storage from file \"storage.txt\"");
            _handlers["erase" ] = make_pair(static_cast<handler_type>(&Job::cmd_erase), "erase one book from storage");
            _handlers["list"  ] = make_pair(static_cast<handler_type>(&Job::cmd_list),  "display storage content");
        }

        // так класс превращается в функциональный объект.

        // Job job1;
        // ...
        // job1();

        void cdecl operator()()
        {
            // полезный приём для избежания break внутри цикла. Облегчает чтение кода
            // описание функции в теле класса даёт указание раскрывать её как inline
            while (enter_cmd())
            {
                handler_map_type::iterator iter = _handlers.find(_cmd);

                // оператор map<...>::operator[] добавляет элемент, если он не найден в списке,
                // а нам этого совсем не надо. Поэтому используем  поиск по ключу
                if (iter!=_handlers.end())
                {
                    try
                    {
                        // програмное исключение exception не приводит к неработоспособности этого кода,
                        // поэтому останавливаем их обработку здесь с чистым сердцем.
                        // часто люди злоупотребляют блоками try..catch и оставляют работать
                        // повреждённую программу.

                        (this->*iter->second.first)();
                    }
                    catch(exception& e)
                    {
                        clog << "runtime error: " << e.what() << endl;
                    }
                }
                else
                {
                    cout << "Unexpected command! Type \"help\" for list of avaible commands\n";
                }
            }

            cout << "Bye!";
        }

        // далее пошли наши команды

        void cdecl cmd_help()
        {
            // прикольно выглядит:
    //        cout.fill('.');
            cout.flags(ios::left);

            // map<> держит ключи в сортированном виде, поэтому вывод будет в
            // алфавитном порядке

            for (handler_map_type::iterator
                    first=_handlers.begin(), last=_handlers.end(); first!=last; ++first)
            {
                cout.width(10);
                cout << first->first << first->second.second << "\n";
            }
        }

        void cdecl cmd_list()
        {
            // вот так просто теперь выглядит вывод
            // ostream_iterator<> использует оператор << для вывода в поток
            copy(_books.begin(), _books.end(), ostream_iterator<Book,char>(cout));
        }

        void cdecl cmd_add()
        {
            // небезопасно читаем новую книгу
            // и безопасно добавляем в контейнер
            Book new_book;
            new_book.human_read_unsafe();
            _books.push_back(new_book);
        }

        void cdecl cmd_clear()
        {
            _books.clear();
        }

        void cmd_save()
        {
            // вот так просто теперь выглядит сохранение
            ofstream out("storage.txt");
            copy(_books.begin(), _books.end(), ostream_iterator<Book,char>(out));
        }

        void cdecl cmd_load()
        {
            // загружаем в отдельный список - безопасно при исключениях
            list<Book> new_list;
            ifstream in("storage.txt");
            copy(istream_iterator<Book,char>(in), istream_iterator<Book,char>(), back_inserter(new_list));

            if (!in.eof())
            {
                in.clear(ios::goodbit);
                string error_word;
                getline(in, error_word);

                clog << "Job::cmd_load parser error before: " << error_word << endl;
                throw runtime_error("Job::cmd_load parser error");
            }

            // меняем местами содержимое списков. При чистке стека старый список удалится.
            new_list.swap(_books);
        }

        void cdecl cmd_erase()
        {
            // remove_if перемещает в конец списка все элементы, которые удовлетворяют условию.
            // если до выполнения erase произойдёт исключение, книги не потеряются. Но правда
            // изменится порядок.
            _books.remove_if<title_predicate>(/*это вызов конструктора*/ title_predicate());
        }
    };


    // вот теперь настал тот момент, когда непонятно, что кончилось и что началось,
    // если помещать описание класса внутрь функции.
    Job()();
}
//------------------------------
Это сообщение посчитали полезным следующие участники:

Отправлено: 12:50, 24-11-2008 | #7


Аватара для Drongo

Будем жить, Маэстро...


Moderator


Сообщения: 6695
Благодарности: 1391

Профиль | Сайт | Отправить PM | Цитировать


pva, Спасибо большое!!! Хотя задача и вопрос не мой, но мне он интересен тоже! Всё компилируется и работает без нареканий.

P.S. Вот только автор темы куда-то пропал...

-------
Правильная постановка вопроса свидетельствует о некотором знакомстве с делом.
3нание бывает двух видов. Мы сами знаем предмет — или же знаем, где найти о нём сведения.
[Quick Killer 3.0 Final [OSZone.net]] | [Quick Killer 3.0 Final [SafeZone.cc]] | [Парсер логов Gmer] | [Парсер логов AVZ]


Отправлено: 14:21, 24-11-2008 | #8


Новый участник


Сообщения: 14
Благодарности: 0

Профиль | Отправить PM | Цитировать


Извиняюсь, что не мог ответить на ПК попал какой то странный червь(dr web его не обнаружил) пришлось сносить систему.
Огромное спасибо! В принципе мне бы хватило и маленького дописания кода без пабликов и приватов, очень благодаре, так как они как раз у меня в ледующих лабораторках будут, а теперь можно будет посмотреть принцип их работы.

Отправлено: 00:33, 25-11-2008 | #9


Новый участник


Сообщения: 14
Благодарности: 0

Профиль | Отправить PM | Цитировать


Еще вопрос у меня не кампилируется какой проект нужно создавать? просто нас учили создавать проект С++ win32 console aplication а в нем файл по умолчанию stdafx.h и он ругатся из - за его отсутствия

http://forum.oszone.net/attachment.p...1&d=1227631655

Отправлено: 19:40, 25-11-2008 | #10



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C/C++ - [решено] Помогите дорешать задачу с созданием класса Book

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
C/C++ - Помогите решить задачу Urann Программирование и базы данных 11 14-05-2013 11:44
C/C++ - [решено] Помогите решить задачу по С++ Spl1t Программирование и базы данных 301 21-12-2011 23:20
C/C++ - Помогите решить задачу zdughi Программирование и базы данных 1 01-05-2009 06:03
помогите решить задачу snikers Программирование и базы данных 2 27-08-2004 01:57
помогите решить задачу Guest Программирование и базы данных 2 21-05-2004 11:23




 
Переход