CS 112
Introduction to Data Structures
Week 03
Classes and Operator Overloading
Eric Araújo
Calvin University · Fall 2026
public, private, constA class bundles data with the operations that make sense on it, and hides how the data is actually stored.
int x = 7;
double d = 3.14;
One name, one box.
Pair p(7, 11);
Enemy goblin("green", 3);
One name, one box, built from smaller boxes side by side.
p is not a pointer and not a list. It is one variable, exactly like x, just as wide as its members put together.
class Pair {
public:
Pair();
Pair(Item f, Item s);
Item getFirst() const;
Item getSecond() const;
private:
Item myFirst;
Item mySecond;
};
#include "Pair.h"
Pair::Pair() {
myFirst = 0;
mySecond = 0;
}
Item Pair::getFirst() const {
return myFirst;
}
Pair:: DoesThe header said the function exists. The .cpp has to say which class it belongs to, or you have written something else entirely.
// Pair.cpp
Item getFirst() const {
return myFirst; // error: not declared
} // error: const, no object
// Pair.cpp
Item Pair::getFirst() const {
return myFirst; // this object's member
} // const: will not change it
:: is the scope resolution operator. Read Pair::getFirst right to left: "getFirst, the one that belongs to Pair". It is the same thing you read in compiler messages, where std::string is "string, the one that belongs to std".public and privatePair.h
class Pair {
public:
Pair(Item f, Item s);
Item getFirst() const;
private:
Item myFirst;
Item mySecond;
};
main.cpp
#include "Pair.h"
...
int main() {
Pair p(7, 11);
...
p.getFirst(); // it is public
p.myFirst; // it is private
// compile error
}
// Pair.h, before // Pair.h, after
private: private:
Item myFirst; Item myItems[2];
Item mySecond;
Pair.h and Pair.cpp onlyPair.h, Pair.cpp, and every program that uses PairPair.cpp only: the header does not mention storagePair, but not Pair itselfprivate, so no program outside Pair was ever allowed to name myFirst. Nothing out there can break, because nothing out there was reaching in. You rewrite the private half of the header and the methods that use it; the public half, the part you promised, does not move. That is what private buys you.
const Methods
Item getFirst() const; // promises not to change it
void setFirst(Item v); // may change it
const goes after the parameter listconst method, assigning to a member is a compile errorconst methods may be called on a const object
class Pair {
public:
Pair(); // same name, no return type
Pair(Item f, Item s); // a second one, other parameters
...
private:
Item myFirst; // a constructor must leave these
Item mySecond; // two in a valid state, never junk
};
Pair a; // creating it runs Pair()
Pair b(7, 11); // this one runs Pair(Item, Item)
class Pair {
public:
~Pair(); // no args, no return
};
It runs automatically when the object dies:
delete is called on itPair you don't need to write one. Next week, when objects start owning memory from new, you will, and forgetting it leaks.
TEST_CASE("Pair stores what we give it") {
Pair p(7, 11);
REQUIRE(p.getFirst() == 7);
REQUIRE(p.getSecond() == 11);
}
TEST_CASE("default Pair starts at zero") {
Pair p;
REQUIRE(p.getFirst() == 0);
}
Building a class from its tests.
(private means private, even to the person who typed it)
Pair p(7, 11);
cout << p << endl;
error: no match for 'operator<<' (operand types are 'std::ostream' and 'Pair')
cout was written years before your class existed. Nobody ever told it what a Pair looks like.
cout << p.getFirst() << ", "
<< p.getSecond() << endl;
Every time. In every program. In every loop that prints a hundred of them.
cout once what a Pair looks like, and cout << p works everywhere after that. That is the whole of operator overloading.<<
void operator<<(ostream &out, const Pair &p) {
out << "(" << p.getFirst() << ", "
<< p.getSecond() << ")";
}
cout << p; // ✓ works
ostream, not your object.<<
ostream& operator<<(ostream &out, const Pair &p) {
out << "(" << p.getFirst() << ", "
<< p.getSecond() << ")";
return out; // hand the stream back
}
cout << p << endl; // ✓ now this works
cout << p << endl is really (cout << p) << endl. If the first call returns
void, there's no stream left for endl. Returning ostream& is what makes chaining work.
operator<< return ostream& rather than ostream?
endl can be usedcout is globalEnemyprivate? Which methods are const? What is the first test you would write?.h declares the interface, .cpp holds the implementationprivate data buys you the freedom to change how it's storedostream& from operator<< so output can chain
This deck stands on earlier CS112 materials by
Joel Adams and Victor Norman · adapted and extended by Eric Araújo