Before You Start
We've come quite a long way. So far, we've learned:
- How to make classes in C++.
- How to define our own versions of C++ operators for our class to make our classes look and behave like C++'s built-in
- Examples:
=
,<<
, and even[…]
.
- Examples:
- How to allocate memory dynamically on the heap.
- Either single objects or arrays.
- The dynamic-train assignment showed us that
- Successively doubling data-structure capacity is a better way to handle growth than fixed-size capacity bumps.
- What things we need to be careful about when growing objects.
- Two ways to loop over an array:
- Using an integer index.
- Start/past-the-end pointers.
Looking Back at Our CheckList
Class
At the end of our last lesson, we'd made a CheckList
class with this header:
#ifndef CHECKLIST_HPP_INCLUDED
#define CHECKLIST_HPP_INCLUDED
#include <string>
#include <iostream>
class CheckList {
public:
CheckList() = delete;
CheckList(std::string task, size_t numSteps);
CheckList(const CheckList& other);
~CheckList();
CheckList& operator=(CheckList rhs); // copy-and-swap idiom
void swap(CheckList& other);
std::string task() const;
size_t size() const;
// We provide two overloaded operator[], one for writable checklists
// and one for read-only ones.
std::string& operator[](size_t index);
const std::string& operator[](size_t index) const;
std::string* begin();
std::string* end();
const std::string* begin() const;
const std::string* end() const;
void printToStream(std::ostream& out) const;
private:
std::string task_;
size_t numSteps_;
std::string* steps_;
};
// We'll provide a global function that adds two checklists
CheckList operator+(const CheckList& lhs, const CheckList& rhs);
// We'll provide a global function that prints a checklist.
std::ostream& operator<<(std::ostream& out, const CheckList& list);
#endif // CHECKLIST_HPP_INCLUDED
You can explore the rest of the code and run it on Codiva.
The begin()
and end()
member functions return a pointer to the internal array of CheckList
items, allowing us to go through them with a standard begin/past-the-end loop.
The begin()
and end()
member functions are overloaded based on whether the object they're being invoked on is const (i.e., read-only) or not.
In other words, if we have a CheckList
object called myList
,
- If
myList
isconst
(read-only), and we saymyList.begin()
, it calls theconst
version ofmyList.begin()
, which returns aconst std::string*
(which doesn't allow us to change the checklist items). - If
myList
is non-const
(can be written to),myList.begin()
will call the non-const
version ofbegin*()
that returns astd::string*
(which can be used to both read and write a checklist item).
I know we've done it before, but it still seems weird and confusing to put
const
at the end of the line, not attached to anything, just sort of dangling there….That's how we mark the target object (the object that
this
points to) asconst
.It is a bit of an odd placement, but there's nowhere else the C++ standard could have put it. A
const
at the front of the line specifies the return type, and aconst
inside the parentheses would mark one of the explicit arguments as beingconst
.
(When logged in, completion status appears here.)