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
myListisconst(read-only), and we saymyList.begin(), it calls theconstversion ofmyList.begin(), which returns aconst std::string*(which doesn't allow us to change the checklist items). - If
myListis non-const(can be written to),myList.begin()will call the non-constversion 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
constat 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
thispoints to) asconst.
It is a bit of an odd placement, but there's nowhere else the C++ standard could have put it. A
constat the front of the line specifies the return type, and aconstinside the parentheses would mark one of the explicit arguments as beingconst.
(When logged in, completion status appears here.)