首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于c++( Part2 of A)的图书馆管理系统OOP

基于c++( Part2 of A)的图书馆管理系统OOP
EN

Code Review用户
提问于 2020-10-16 23:33:30
回答 2查看 226关注 0票数 2

我不知道这是否可以接受,但我想感谢社会各界对我在这个项目上的上一篇文章的建议。

这是一个初学者的项目。

图书馆管理系统旨在处理函数库的基本事务,到目前为止,我已经实现了BookItem类,并得到了社区的建议,还实现了一个Date类(不是全部功能)。

图书馆员的课程虽然还没有完成,但也很实用.我用一张清单把书存放在图书馆里。我看到了一些帖子,其中向量被建议为goto数据结构,我觉得列表最适合这种情况,如果向量更好的话,如果您能突出说明原因,我会很感激。

注意:我是一个初学者,对C++的高级主题还不了解。

这是代码

Date.hh

代码语言:javascript
复制
#ifndef DATE_HH
#define DATE_HH
/*****************************************************************
 * Name: Date.hh
 * Author: Samuel Oseh
 * Purpose: Date class method-function prototype
 * ***************************************************************/
#include <iostream>
class Date {
    friend std::ostream &operator<<( std::ostream &, const Date & );
     private:
        /* data-member */
        unsigned int month;
        unsigned int day;
        unsigned int year;

        // utility function
        unsigned int checkDay( int ) const;

    public:
        static const unsigned int monthsPerYear = 12;

        // ctors
        Date() : Date( 1, 1, 1970 ){}
        Date( int m, int d, int y ) { setDate( m, d, y );}
        Date( int m ) : Date( m, 1, 1970 ){}
        Date( int m, int d ) : Date( m, d, 1970 ) {}
        
        // copy operations
        Date( const Date &d ) { *this = std::move(d); }
        Date &operator=( const Date &d ) { month = d.month; day = d.day; year = d.year; return *this; }

        /* method-functions */
        void setDate( int m, int d, int y );
        void setMonth( int m );
        void setDay( int d );
        void setYear( int y );
        unsigned int getMonth() const;
        unsigned int getDay() const;
        unsigned int getYear() const;       
        void nextDay();

        // dtor
        ~Date(){};
};

#endif

Date.cc

代码语言:javascript
复制
/*****************************************************************
 * Name: Date.cc
 * Author: Samuel Oseh
 * Purpose: Date class method-function definitions
 * ***************************************************************/
#include <iostream>
#include <stdexcept>
#include <array>
#include "Date.hh"

void Date::setDate( int m, int d, int y) {
    setMonth( m );
    setDay( d );
    setYear( y );
}

void Date::setDay( int d ) {
    day = checkDay( d );
}

void Date::setMonth( int m ) {
   if ( m >= 1 && m < 13 )
        month = m;
    else
      throw std::invalid_argument( "Month must be between 1-12" );
}

void Date::setYear( int y ) {
    if ( y >= 1970 )
        year = y;
    else
        throw std::invalid_argument( "year must be greater than 1969" );
}

void Date::nextDay() {
    day += 1;
    try {
        checkDay( day );
    } catch ( std::invalid_argument &e ) {
        month += 1;
        day = 1;
    }
    if ( month % 12 == 0 ) {
        year += 1;
        month = 1;
    }
}

std::ostream &operator<<( std::ostream &os, const Date &d ) {
    os << d.month << "/" << d.day << "/" << d.year << " ";

    return os;
} 

// utility function
unsigned int Date::checkDay( int testDay ) const {
    static const std::array < int, monthsPerYear + 1 > daysPerMonth = { 0,31,28,31,30,31,30,31,31,30,32,30,31};

    if ( testDay > 0 && testDay <= daysPerMonth[ month ] )
        return testDay;
    
    if ( month == 2 && testDay == 29 && ( year % 400 == 0 || ( year % 4 == 0 && year % 100 != 0 ) ) )
        return testDay;
    throw std::invalid_argument( "Invalid day for current month and year" );
}

BookItem.hh

代码语言:javascript
复制
#ifndef BOOKITEM_HH
#define BOOKITEM_HH
/*****************************************************************
 * Name: BookItem.hh
 * Author: Samuel Oseh
 * Purpose: BookItem class method-function prototype
 * ***************************************************************/
#include <string>
#include "Date.hh"

enum class BookStatus : unsigned { RESERVED, AVAILABLE, UNAVAILABLE, REFERENCE, LOANED, NONE };
enum class BookType : unsigned { HARDCOVER, MAGAZINE, NEWSLETTER, AUDIO, JOURNAL, SOFTCOPY };

class BookItem {
    private:
        /* data-members */
        std::string title;
        std::string author;
        std::string category;
        Date pubDate;
        std::string isbn;
        BookStatus status;
        BookType type;
    public:
        // ctor
        BookItem() = default;
        BookItem( const std::string &title, const std::string &author, const std::string &cat, const Date &pubDate, \
                const std::string &isbn, const BookStatus status, const BookType type ); 

        // copy operations
        const BookItem& operator=( const BookItem &bookItem );
        BookItem( const BookItem &bookItem ) { *this = std::move(bookItem); }

        /* method-functions */
        void setStatus( BookStatus s ) { status = s; };
        void setType( BookType t ) { type = t;};
        std::string getStatus() const;
        std::string getType() const;
        std::string getTitle() const { return title; }
        std::string getAuthor() const { return author; }
        Date &getPubDate() { return pubDate; }
        void printPubDate() const { std::cout << pubDate; }
        std::string getIsbn() const { return isbn; }
        void setCategory( const std::string &c ) { category = c; }
        std::string getCategory() const { return category; };

        // dtor
        ~BookItem(){}
};
#endif

BookItem.cc

代码语言:javascript
复制
/*****************************************************************
 * Name: BookItem.cc
 * Author: Samuel Oseh
 * Purpose: BookItem class method-function definitions
 * ***************************************************************/
 #include <iostream>
#include "BookItem.hh"

BookItem::BookItem( const std::string &t, const std::string &a, const std::string &c, const Date &d, \
                const std::string &i, const BookStatus s, const BookType ty ) {
                    title = t, author = a, category = c, pubDate = d, isbn = i;
                    setStatus( s );
                    setType( ty );
}

const BookItem &BookItem::operator=( const BookItem &bookItem ) {
    title = bookItem.title;
    author = bookItem.author;
    category = bookItem.category;
    pubDate =  bookItem.pubDate;
    isbn = bookItem.isbn;
    status = bookItem.status;
    type = bookItem.type;

    return *this;
}

std::string BookItem::getStatus() const { 
    if ( status == BookStatus::AVAILABLE )
        return "AVAILABLE";
    else if ( status == BookStatus::REFERENCE )
        return "REFERENCE";
    else if ( status == BookStatus::UNAVAILABLE )
        return "UNAVAILABLE";
    else if ( status == BookStatus::LOANED )
        return "LOANED";
    else if ( status == BookStatus::RESERVED )
        return "RESERVED";
    else
        return "NONE";
} 

std::string BookItem::getType() const {
    if ( type == BookType::AUDIO )
        return "AUDIO";
    if ( type == BookType::HARDCOVER )
        return "HARDCOVER";
    if ( type == BookType::JOURNAL )
        return "JOURNAL";
    if ( type == BookType::MAGAZINE )
        return "MAGAZINE";
    if ( type == BookType::NEWSLETTER )
        return "NEWSLETTER";
    if ( type == BookType::SOFTCOPY )
        return "SOFTCOPY";
    else 
        return "NONE";
}

Librarian.hh

代码语言:javascript
复制
#ifndef LIBRARIAN_HH
#define LIBRARIAN_HH
/*****************************************************************
 * Name: Librarian.hh
 * Author: Samuel Oseh
 * Purpose: Librarian class method-function prototype
 * ***************************************************************/
#include <iostream>
#include <string>
#include "BookItem.hh"
#include <list>

class Librarian {
    private:
        /* data-member */
        std::string name;
        Date dateOfHire;
        std::list<BookItem> *books = new std::list<BookItem>;
    public:
        // ctor
        Librarian() = default;
        Librarian( const std::string &name, const Date &dateOfHire );

        /* basic method-functions */
        void setName( const std::string &name );
        void setDateOfHire( const Date &date );
        std::string getName() const { return name; };
        Date &getDateOfHire() { return dateOfHire; }
        void printDateOfHire() const { std::cout << dateOfHire; }

        /* core functionalities */
        void addBook( const BookItem &book );
        void auditLibrary() const;
        
        // dtor
        ~Librarian(){}
};

#endif

Librarian.cc

代码语言:javascript
复制
/*****************************************************************
 * Name: Librarian.cc
 * Author: Samuel Oseh
 * Purpose: Librarian class method-function definitions
 * ***************************************************************/

#include <iostream>
#include "Librarian.hh"

Librarian::Librarian( const std::string &n, const Date &d ) {
    name = n;
    dateOfHire = d;
}

void Librarian::setName( const std::string &n ) {
    name = n;
}

void Librarian::setDateOfHire( const Date &d) {
    dateOfHire = d;
}

void Librarian::addBook( const BookItem &book ) { 
    if ( books->empty() ) {
        books->push_front( book );
        return;
    }
    for ( auto bk = books->begin(); bk != books->end(); ++bk ) {
        if( book.getTitle() <= bk->getTitle() ) {
            books->insert(bk, book);
            return;
        }
    }
    books->push_back( book );
}

void Librarian::auditLibrary() const {
    std::cout << "Librarian: " << name << ", Date of hire: " << dateOfHire;
    std::cout << "\n\nBooks:";
    for ( auto bk = books->begin(); bk != books->end(); ++bk ) {
        std::cout << "\nName of book: " << bk->getTitle();
        std::cout << "\nAuthor of book: " << bk->getAuthor();
        std::cout << "\nBook category: " << bk->getCategory();
        std::cout << "\nPublication date: ";
        bk->printPubDate();
        std::cout << "\nISBN number: " << bk->getIsbn();
        std::cout << "\nStatus of book: " << bk->getStatus();
        std::cout << "\nType of book: " << bk->getType();
        std::cout << "\n\n"; 
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2020-10-17 07:43:07

constexpr

因为monthsPerYear是一个编译时间常数,所以您应该声明它为constexpr而不是const有关以下内容的更多细节,请参见此答案。constexpr

Date构造函数

Date构造函数采用int,而数据成员是unsigned int

您的构造函数正在调用同一个类的另一个构造函数,该构造函数反过来调用setDate方法,这似乎是一个毫无意义的路径。通常,您会像这样初始化数据成员:

代码语言:javascript
复制
Date():
     d{1}, m{1}, y{1970}
{
   /// EMPTY
}

Date(unsigned int d, unsigned int m, unsigned int y):
          d{d}, m{m}, y{y}
{
   /// EMPTY
} 

注意,对setDate方法的调用现在是多余的吗?虽然内置类型并不重要,但如果有大量用户定义的类型,可能会降低性能,因为它们将是默认构造的。有关成员初始化列表的更多信息

setDate的调用可以替换为调用名为validateDate()的方法,该方法的唯一目的是验证日期,而不是验证和设置数据成员的值。您的许多成员函数可以通过使用validateDate()来简化。

另外,我质疑最后两个用户提供的类型的用途。问问自己,只设置日期或日期/月的用例是什么?

因为您的类只包含内置类型,所以可以省略主体,只需将其声明为default

代码语言:javascript
复制
Date(const Date& rhs) = default;

看这里有什么default有吗?

副本赋值操作符也是如此。

当涉及到内置类型时,使用std::move几乎是毫无意义的。无论如何,您可能不希望在复制构造函数中使用std::move

checkDay

在设置year之前,要使用它的值。

我会把这个条件放在它自己的方法中来检查它是否是闰年。

代码语言:javascript
复制
bool isLeapYear() const
{
     return year % 400 == 0 || ( year % 4 == 0 && year % 100 != 0 );
}

然后,您的checkDay条件可以编写得更简洁一些,如

代码语言:javascript
复制
if((testDay <= daysPerMonth[month]) || (isLeapYear() && month == 2 && testDay == 29))

isLeapYear()放在第一位有助于短路评估,这意味着如果isLeapYear()失败,其余的条件将不会被检查。

此外,考虑返回一个bool,不管测试是否成功,并让方法的调用方决定如果测试失败,应该做什么(例如抛出无效的参数异常)。

BookItem

同样,使用成员初始化列表来设置数据成员。对复制构造函数和复制赋值操作符使用default

使用switch语句

在getStatus和getType方法中,使用开关语句。他们很适合这种情况,看上去要干净得多。

代码语言:javascript
复制
switch(status)
{
    case BookStatus::AVAILABLE:
        return "AVAILABLE";
    case BookStatus::Reference:
        return "REFERENCE";
    ...
    default:
        return "NONE";
}

使用default进行析构器

因为您的BookItem析构函数没有做任何重要的事情,所以您应该声明它为default,并让编译器来处理它。

代码语言:javascript
复制
~BookItem() = default;

返回const std::string&std::string_view

getter方法返回std::string的副本,这不是您一直想要的东西(特别是对于getter)。std::string在堆上分配(嗯,有时不;如果您感兴趣的话查找小字符串优化),这意味着每次调用getter时,都会在堆上分配和释放内存。如果您不打算更改数据,那么您只是在浪费时间构建一个新的字符串。

考虑返回一个const引用const std::string&,或者如果您有符合C++17的编译器,则返回一个std::string_view请看这里std::string_view

std::vector std::list

我明白为什么您需要std::list,因为您希望以排序的方式插入书籍。但是,在几乎所有的情况下,您都希望在std::vector上使用std::list。事实上,我认为您从来不需要std::list而不是std::vector。这与std::vector连续存储元素的事实有关,这得益于被称为cache locality的东西。这是一个答案本身,所以你应该看到这一点。缓存友好代码

您可以在std::sort上使用std::vector,并使用lambda作为自定义比较器。

代码语言:javascript
复制
std::vector<BookItem> inventory.
inventory.push_back(...);
inventory.push_back(...);
...

std::sort(inventory.begin(), inventory.end(), [](const BookItem& a, const BookItem& b){ return a.getTitle() < b.getTitle(); });

new关键字

除非您有充分的理由,否则您希望使用STL提供的智能指针,而不是使用newdelete。现在,您的代码正在泄漏内存,因为您还没有在列表中调用delete,这是使用原始newdelete的最大缺陷。

为什么要使用分配堆上的列表呢?默认情况下,std::list在堆上分配;没有理由在堆上分配list对象。

票数 2
EN

Code Review用户

发布于 2020-10-17 00:39:46

代码语言:javascript
复制
unsigned int month;
unsigned int day;
unsigned int year;

也可以写成一行

代码语言:javascript
复制
unsigned int month, day, year;

您使用无符号int,没关系,但仍然应该使用int,特别是对于小数字。无符号int主要用于将数据从存储设备/网络流传送到存储设备/网络流。有符号int更好地移植,因为其他一些编程语言不支持无符号int。签名的int通常允许更容易的调试,因为a-1通常比4294967295更容易检测.

代码语言:javascript
复制
std::string BookItem::getStatus() const { 
    if ( status == BookStatus::AVAILABLE )
        return "AVAILABLE";
    else if ( status == BookStatus::REFERENCE )
        return "REFERENCE";
    else if ( status == BookStatus::UNAVAILABLE )
        return "UNAVAILABLE";
    else if ( status == BookStatus::LOANED )
        return "LOANED";
    else if ( status == BookStatus::RESERVED )
        return "RESERVED";
    else
        return "NONE";
}

这也可以写成:

代码语言:javascript
复制
std::string BookItem::getStatus() const { 
    if ( status == BookStatus::AVAILABLE ) return "AVAILABLE";
    if ( status == BookStatus::REFERENCE ) return "REFERENCE";
    if ( status == BookStatus::UNAVAILABLE ) return "UNAVAILABLE";
    if ( status == BookStatus::LOANED ) return "LOANED";
    if ( status == BookStatus::RESERVED ) return "RESERVED";
    return "NONE";
}

您也可以使用开关/机箱。

使用时:

代码语言:javascript
复制
std::cout << "\nAuthor of book: " << bk->getAuthor();
std::cout << "\nBook category: " << bk->getCategory();

最好这样做:

代码语言:javascript
复制
std::cout << "Author of book: " << bk->getAuthor() << std::endl;
std::cout << "Book category: " << bk->getCategory() << std::endl;

否则,您的代码在第一个视图中看起来很好。也许添加一些额外的标题文本描述。

票数 -2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/250769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档