CS 70

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 […].
  • 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.

Explain what the begin() and end() member functions do for a CheckList object, and why there are two pairs of them, one pair of begin/end that works on writable CheckLists and one that works on read-only CheckLists.

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 is const (read-only), and we say myList.begin(), it calls the const version of myList.begin(), which returns a const 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 of begin*() that returns a std::string* (which can be used to both read and write a checklist item).
  • Hedgehog speaking

    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….

  • LHS Cow speaking

    That's how we mark the target object (the object that this points to) as const.

  • RHS Cow speaking

    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 a const inside the parentheses would mark one of the explicit arguments as being const.

(When logged in, completion status appears here.)