Ch13.1: What a Class Is: Data and the Functions That Belong to It

中文(简体)

1. Data, and the Functions That Work on It

Here is a point, written the way you have written everything so far: a struct with two members, and a free function that takes one:


struct point_data
{
	::std::int_least32_t x;
	::std::int_least32_t y;
};

::std::int_least32_t sum_of(point_data const &p) noexcept
{
	return p.x + p.y;
}

This works, and it has a real weakness that has nothing to do with style: nothing connects the two. The function has to name the type twice, and it has to be written in a place where the type is visible. Nothing in the compiler knows that sum_of is the sum of a point, so there is no way to discover the operations a point supports — you grep for them and hope.

Now the same thing as a class. The two integers and the two operations live inside one type, and the operations are called with the dot syntax you already use for ::fast_io::string members:


#include <fast_io.h>
#include <cstdint>
using namespace ::fast_io::io;

// The old way: the data and the functions that work on it are separate.
struct point_data
{
	::std::int_least32_t x;
	::std::int_least32_t y;
};

::std::int_least32_t sum_of(point_data const &p) noexcept
{
	return p.x + p.y;
}

// A class: the data and the operations that belong to it, together, in one type.
class point
{
public:
	::std::int_least32_t x{};
	::std::int_least32_t y{};

	::std::int_least32_t sum() const noexcept
	{
		return x + y;
	}
	::std::int_least32_t product() const noexcept
	{
		return x * y;
	}
};

int main()
{
	point a{3, 4};
	point b{-2, 5};

	println("a.sum()     = ", a.sum());
	println("a.product() = ", a.product());
	println("b.sum()     = ", b.sum());
	println("b.product() = ", b.product());

	point_data d{3, 4};
	println("sum_of(d)   = ", sum_of(d));
}

a.sum()     = 7
a.product() = 12
b.sum()     = 3
b.product() = -10
sum_of(d)   = 7

Nothing was added to the language. sum() is an ordinary function that happens to be declared inside the class, and a.sum() is an ordinary function call that also says on which object to run it. That is the whole mechanism: a member function is a function with one extra, invisible argument — the object (Ch13.7).

Vocabulary, once. The variables inside a class are data members (or fields, or attributes). The functions inside a class are member functions (or methods). A variable of a class type is an object (or an instance) of that class. C++ programmers use both words for both things, so this tutorial says “data member” and “member function” when the distinction matters.

2. An Object Is a Value Like Any Other

This is the part that surprises people coming from languages where objects are special: here a point is not a reference, a handle, or a pointer. It is its two integers, stored where you put it, with no extra allocation. So everything you learned about values keeps working:


#include <fast_io.h>
#include <cstdint>
#include <cstddef>
using namespace ::fast_io::io;

class point
{
public:
	::std::int_least32_t x{};
	::std::int_least32_t y{};

	::std::int_least32_t sum() const noexcept
	{
		return x + y;
	}
};

::std::int_least32_t total(point const *ps, ::std::size_t n) noexcept
{
	::std::int_least32_t sum{};
	for (::std::size_t i{}; i != n; ++i)
	{
		sum += ps[i].sum();
	}
	return sum;
}

int main()
{
	point ps[3]{{1, 2}, {3, 4}, {5, 6}};
	for (auto const &p : ps)
	{
		println(p.x, " + ", p.y, " = ", p.sum());
	}
	println("total = ", total(ps, 3));
}

1 + 2 = 3
3 + 4 = 7
5 + 6 = 11
total = 21

Notice that total is a free function that knows nothing about points beyond what the class shows it: ps[i].sum(). That is the usual shape of a program built out of classes — small types that know a lot about themselves, and free functions that combine them.

3. What the Class Actually Buys You

With a struct and free functions With a class and member functions
The operations are scattered; finding them means searching the program. The operations are listed inside the type. Its definition is its documentation.
Any code anywhere can set p.x = -999 and break whatever the data was supposed to mean. You decide who may touch x (Ch13.4).
A new invariant (“coordinates are never negative”) has to be checked at every call site. The invariant is checked once, inside the class (Ch13.5).
Changing the representation means changing every function that took the struct apart. Only the class changes; the interface stays (Ch13.5).
Nothing forces the object to start in a sensible state. Constructors force it (Ch13.6).
In this subchapter the data members are still public. That is deliberate: point a{3, 4}; only works like that because the class has no constructor and all members are public, so the braces initialize the members in declaration order. Chapter 13.4 closes the door and Chapter 13.6 gives you the constructor that replaces this syntax. From then on, the way to make an object is a constructor call, not a brace list you have to keep in sync by hand.