我不知道这是否可以接受,但我想感谢社会各界对我在这个项目上的上一篇文章的建议。
这是一个初学者的项目。
图书馆管理系统旨在处理函数库的基本事务,到目前为止,我已经实现了BookItem类,并得到了社区的建议,还实现了一个Date类(不是全部功能)。
图书馆员的课程虽然还没有完成,但也很实用.我用一张清单把书存放在图书馆里。我看到了一些帖子,其中向量被建议为goto数据结构,我觉得列表最适合这种情况,如果向量更好的话,如果您能突出说明原因,我会很感激。
注意:我是一个初学者,对C++的高级主题还不了解。
这是代码
#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/*****************************************************************
* 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" );
}#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/*****************************************************************
* 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";
}#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/*****************************************************************
* 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";
}
}发布于 2020-10-17 07:43:07
constexpr因为monthsPerYear是一个编译时间常数,所以您应该声明它为constexpr而不是const。有关以下内容的更多细节,请参见此答案。constexpr
Date构造函数Date构造函数采用int,而数据成员是unsigned int。
您的构造函数正在调用同一个类的另一个构造函数,该构造函数反过来调用setDate方法,这似乎是一个毫无意义的路径。通常,您会像这样初始化数据成员:
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。
Date(const Date& rhs) = default;副本赋值操作符也是如此。
当涉及到内置类型时,使用std::move几乎是毫无意义的。无论如何,您可能不希望在复制构造函数中使用std::move。
checkDay在设置year之前,要使用它的值。
我会把这个条件放在它自己的方法中来检查它是否是闰年。
bool isLeapYear() const
{
return year % 400 == 0 || ( year % 4 == 0 && year % 100 != 0 );
}然后,您的checkDay条件可以编写得更简洁一些,如
if((testDay <= daysPerMonth[month]) || (isLeapYear() && month == 2 && testDay == 29))将isLeapYear()放在第一位有助于短路评估,这意味着如果isLeapYear()失败,其余的条件将不会被检查。
此外,考虑返回一个bool,不管测试是否成功,并让方法的调用方决定如果测试失败,应该做什么(例如抛出无效的参数异常)。
BookItem同样,使用成员初始化列表来设置数据成员。对复制构造函数和复制赋值操作符使用default。
switch语句在getStatus和getType方法中,使用开关语句。他们很适合这种情况,看上去要干净得多。
switch(status)
{
case BookStatus::AVAILABLE:
return "AVAILABLE";
case BookStatus::Reference:
return "REFERENCE";
...
default:
return "NONE";
}default进行析构器因为您的BookItem析构函数没有做任何重要的事情,所以您应该声明它为default,并让编译器来处理它。
~BookItem() = default;const std::string&或std::string_viewgetter方法返回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作为自定义比较器。
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提供的智能指针,而不是使用new和delete。现在,您的代码正在泄漏内存,因为您还没有在列表中调用delete,这是使用原始new和delete的最大缺陷。
为什么要使用分配堆上的列表呢?默认情况下,std::list在堆上分配;没有理由在堆上分配list对象。
发布于 2020-10-17 00:39:46
unsigned int month;
unsigned int day;
unsigned int year;也可以写成一行
unsigned int month, day, year;您使用无符号int,没关系,但仍然应该使用int,特别是对于小数字。无符号int主要用于将数据从存储设备/网络流传送到存储设备/网络流。有符号int更好地移植,因为其他一些编程语言不支持无符号int。签名的int通常允许更容易的调试,因为a-1通常比4294967295更容易检测.
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::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";
}您也可以使用开关/机箱。
。
使用时:
std::cout << "\nAuthor of book: " << bk->getAuthor();
std::cout << "\nBook category: " << bk->getCategory();最好这样做:
std::cout << "Author of book: " << bk->getAuthor() << std::endl;
std::cout << "Book category: " << bk->getCategory() << std::endl;否则,您的代码在第一个视图中看起来很好。也许添加一些额外的标题文本描述。
https://codereview.stackexchange.com/questions/250769
复制相似问题