Apologetics Library
Apologetic question: "The Christian intellectual tradition"
Project 10 — Apologetics Library
“Of making many books there is no end, and much study is a weariness of the flesh.” — Ecclesiastes 12:12
(And yet the books are how the tradition has been preserved.)
Chapter: 10 — Constructors, Destructors, and Encapsulation
Due: End of Week 10
Submit: A link to your code — an OnlineGDB project URL or a public GitHub repo URL — with library.cpp as the main source file. See Appendix D for the full workflow.
Allowed tools: Everything through Chapter 10 — classes, constructors, destructors, composition, const methods.
Required (Normal) tier uses only Chapter 10 tools. Some Medium-tier features preview Ch 11 (pointers as return values from finders — see M1’s preview note). No inheritance (Ch 12) or dynamic allocation anywhere.
The Setup
The Christian intellectual tradition is — among many other things — a library. Augustine. Aquinas. Luther. Calvin. Edwards. Spurgeon. Lewis. Schaeffer. Sproul. Plantinga. Wright. Keller. Habermas. The McGrews. Behind every modern apologetics conversation are centuries of careful work that someone preserved, catalogued, and lent.
This project builds the lending mechanism. You’ll write a small Library of apologetics resources, members who check them out, and the rules that keep the whole thing honest.
Real data, again. The library is seeded with real books that exist. Look up titles and authors before you type. If you don’t recognize them, that’s fine — but use real ones.
Suggested starter list (deliberately ecumenical — Lutheran, Reformed, Anglican, Roman Catholic, Evangelical):
- Mere Christianity — C.S. Lewis (Anglican)
- Orthodoxy — G.K. Chesterton (Roman Catholic)
- Confessions — Augustine of Hippo
- Knowledge and Christian Belief — Alvin Plantinga (Reformed)
- The Reason for God — Timothy Keller (Reformed)
- On Guard — William Lane Craig (Evangelical)
- The Resurrection of the Son of God — N.T. Wright (Anglican)
- Pensées — Blaise Pascal (Roman Catholic / Jansenist)
- The Discarded Image — C.S. Lewis (Anglican)
- Law and Gospel — C.F.W. Walther (LCMS)
- This Is My Body — Hermann Sasse (Confessional Lutheran)
- On Being a Theologian of the Cross — Gerhard Forde (Lutheran)
- History, Law, and Christianity — John Warwick Montgomery (Lutheran)
You don’t have to use exactly these. Use real ones. Aim for at least one source from your own confessional tradition.
Learning Targets
By completing this project, you will demonstrate that you can:
- Design a multi-class system:
Book,Member,Library. - Use parameterized constructors and destructors.
- Use composition — one class containing collections of another.
- Write
constmethods on read-only operations. - Coordinate state across multiple objects.
- Use initializer lists.
Normal Tier
Goal: Two classes (Book and Member) that interact via check-out / return operations. Demonstrate in main with at least 4 books and 2 members.
Required features
-
Bookclass:class Book { private: string title; string author; bool available; public: Book(); Book(string t, string a); void check_out(); void return_book(); bool is_available() const; string get_title() const; string get_author() const; void print() const; ~Book(); };Constructor sets
available = true.check_out()setsavailable = false.return_book()sets it back. Destructor prints"[Book '[title]' removed.]"only whentitleis non-empty — otherwise it prints nothing. (Reason: Medium tier’sLibrarydeclaresBook books[50], which default-constructs 50 emptyBooks; you don’t want 50[Book '' removed.]lines blasted at the grader when the program ends.) -
Memberclass:class Member { private: string name; string borrowed_titles[10]; int borrowed_count; public: Member(); Member(string n); bool borrow(Book& book); bool return_to_library(string title, Book& book); void list_borrowed() const; string get_name() const; ~Member(); };borrowchecks the book’s availability and the member’s slot count; on success, calls the book’scheck_out()and records the title.return_to_libraryfinds the title inborrowed_titles, removes it (shift the rest down), and callsbook.return_book().Destructor prints
"[Member [name] left the library.]"only whennameis non-empty (same reason asBook:LibrarydeclaresMember members[20]). -
A
mainthat demonstrates:- Construct at least 4 books (real titles).
- Construct at least 2 members.
- Have member A borrow 2 books.
- Have member A try to borrow a third already-checked-out book (should fail).
- Have member B borrow a different book.
- Have member A return one book.
- Print every member’s borrowed list.
-
Compiles cleanly with
-Wall -Wextraenabled. No warnings. -
All getters are
constmethods. -
No public fields.
Book::available,Member::name, etc., are all private.
Normal-tier rubric (out of 100)
| Criterion | Points |
|---|---|
Compiles cleanly with -Wall -Wextra | 10 |
Book class with all 8 required methods | 15 |
Member class with all 6 required methods | 15 |
| Default and parameterized constructors present | 10 |
| Destructors print expected messages | 10 |
borrow correctly coordinates Book and Member state | 10 |
return_to_library correctly coordinates | 10 |
Getters are const | 5 |
| All fields are private | 5 |
main demo exercises the required scenarios | 5 |
| Seed data uses real books | 5 |
Medium Tier (+up to 25% extra credit)
M1. The Library container class
Add a third class:
class Library {
private:
Book books[50];
int book_count;
Member members[20];
int member_count;
public:
Library();
void add_book(const Book& b);
void add_member(const Member& m);
Book* find_book(string title); // returns pointer; nullptr on not-found
Member* find_member(string name); // same
void print_summary() const;
~Library();
};
The Library owns all books and members. Adding goes through the library, never directly. The destructor prints "[Library closed: N books, M members.]".
On Book* vs Book&: for finders that may not find anything, a pointer is the clean choice — nullptr is the “not found” signal. References can’t be null, so a reference-returning finder has to either crash or return a sentinel, both of which are ugly. Chapter 11 will formalize pointers; for this project, treat Book* as “either the address of a real Book or nullptr,” check it before you use it, and never delete the returned pointer (the Library owns the storage). See the hints section below.
Crucially: Book must have a default constructor (which you already wrote) because Book books[50] requires one.
M2. Title uniqueness
Modify Library::add_book so it rejects duplicate titles (case-insensitive). Reuse your to_lower helper from Chapter 6 or write a new one.
Modify Library::add_member similarly for unique names.
M3. Aging — due dates
Add an int days_borrowed field to the borrowing tracking. Either store it on the Book (when checked out) or alongside the title in the Member’s borrowed list. Add a Library::age(int days) method that increments every checked-out book’s days_borrowed.
Add a Library::print_overdue(int max_days) const that prints every book that’s been checked out longer than max_days days.
For Medium tier, you don’t need fines yet — just the “overdue” flag.
Hard Tier (+up to 25% additional extra credit)
H1. Fines
Extend the aging system: each day a book is overdue, charge the responsible member a per-day fine. Add a double fines_owed field to Member. Add a Member::pay_fine(double amount) method that subtracts from fines_owed (clamped at 0).
Update Library::age(days) to apply fines.
H2. Generate report
Add Library::generate_report() const. It prints:
- Total books, total members.
- Total fines outstanding across all members.
- The most-borrowed book (track borrow counts per book — add a private field to
Book). - The most-active member (track borrow counts per member).
This is the kind of summary a librarian would actually print at end of month.
H3. The flex move
Find one C++ feature not covered. Strong candidates:
- Member initialization with
= defaultfor explicit default constructors. - Deleted copy constructor (
Library(const Library&) = delete;) to prevent accidental copies of the library — this is a serious design pattern. - An overloaded
<<operator forBooksocout << bookworks. - The
friendkeyword — letting one class access another’s privates. Lewis would have you think carefully before usingfriend; some codebases hate it, others use it sparingly.
Document per Project 1 H4 rules.
Submission
Submit one URL via the course portal:
- OnlineGDB project link (recommended for Coding 1 and Coding 2). Create your project at onlinegdb.com, set compiler flags to
-Wall -Wextrain the project settings, build your solution, and share the link. See Appendix D for the full workflow. - GitHub repo link (optional). If you’ve set up local development on your own, push the source to a public repo and submit that URL. You’re responsible for making sure the code compiles when the grader checks it out.
What the linked project must contain
- The main source file —
library.cpp— containing your full solution. - A reflection comment block at the very top of that file:
/*
* Tier targeted: Normal / Medium / Hard
* Features done: list each feature you completed
* What I learned: one short paragraph (no bullets)
* What I'd change: one sentence
* AI usage: where and how, if any. Be honest.
*/
- The program left in a “demonstrable” state — when the grader presses Run, the features for your targeted tier should be exercised. Hard-code inputs at the top of
main()(or pre-fill OnlineGDB’s Stdin panel) so the grader doesn’t have to guess what to type.
That’s it. No separate demo.txt. No screenshots. The instructor will open your link, read the comment block, run the program, and grade against the rubric.
Coach’s Note — Coding 1 and Coding 2 focus on writing code, not managing development environments. If something behaves oddly, you and the grader are looking at the exact same browser-hosted environment — there are no “works on my machine” defenses by design. Coding 3 will introduce a local toolchain properly.
Hints
- “
Book books[50]won’t compile.” You don’t have a default constructor forBook. Add one:Book() : title(""), author(""), available(true) {}. - “How do I write
find_bookreturningBook*?” Loop throughbooks[0..book_count]; ifbooks[i].get_title() == titlereturn&books[i]. If the loop ends without finding,return nullptr;. At the call site, checkBook* b = lib.find_book("Mere Christianity"); if (b != nullptr) { b->check_out(); }. Don’tdeleteit —Libraryowns the storage. - “The destructor messages are firing in a weird order.” They fire in reverse of construction order. The last-declared object dies first. Inside a class with member objects, the class’s destructor body runs first, then each member’s destructor fires. Add print statements and watch it.
- “My getters won’t compile when called on a
constreference.” Addconstafter the parameter list of every method that doesn’t modify the object. - “I want to use
std::vector<Book>instead ofBook books[50].” Wait until you’ve shipped Normal tier with the fixed-size array. Then experiment with vector as the Hard-tier flex move. The pain of fixed arrays is part of why vector exists. - “How long should this take me?” Normal: 4–6 hours. Medium: 6–10 hours. Hard: 10–16 hours.
What Mastery Looks Like
A great Project 10 has classes that coordinate cleanly. main doesn’t reach into Book or Member private state — it asks them to do things via methods. The classes know how to handle their own state and how to talk to each other.
A great Project 10 has correct lifecycle reasoning. Adding cout to all constructors and destructors and tracing the output, you can predict the order of operations before running it.
A great Project 10 is small and complete. Three classes, one demo main, a couple hundred lines total. The grader can read it end-to-end and understand the whole system in 10 minutes.
A great Project 10 has real data. The books are real. Authors are correctly spelled. A knowledgeable reader could verify your library against their bookshelf.
When You’re Done
- Read
library.cppaloud. The classes are short. The methods are short. The names are clear. - Run the demo. Watch the destructor messages in order. Make sense?
- Update README. Verify your book list.
- Submit.
- Read Chapter 11. Pointers and dynamic memory. The fixed-size-array constraint goes away.
Coach’s Note — This is the project where most students start writing code that “looks like real software.” Three classes coordinating, constructors and destructors managing lifecycle, methods enforcing rules. You’re not there yet — Chapter 11’s pointers and Chapter 12’s inheritance still raise the bar — but if you ship a clean Project 10, you’ve earned the right to be proud of it. Show it to someone.
See you on Monday.