Ch13.2: Member Functions

English

提示:本页尚未翻译,当前显示英文原文。

1. One Object, One Operator at a Time

A member function is a function that is written inside a class, takes the object it was called on as an invisible extra argument, and is called with the dot syntax:


point p{3, 4};
println(p.sum());       // call sum() with p as the object
p.shift_by(10);          // call shift_by(10) with p as the object

Everything else about it is exactly what you learned in Chapter 6: parameters, return type, overloading, default arguments, and the noexcept / throws specification. There is no second, easier language inside a class. A member function is just a function with a subject.

2. Declaring Inside, Defining Outside

Writing the body inside the class is fine for one-line functions. For anything longer the usual style is a declaration inside the class and a definition outside it, prefixed with the class name and :::


class point
{
public:
	::std::int_least32_t sum() const noexcept;   // declaration
};

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

point::sum is the function’s qualified name. The point:: is not decoration: it is what makes this the sum that belongs to point and not some other sum in another class. Inside its own body, and inside every other member of the class, the members are in scope without any prefix: return x + y; needs no this->.

The full program, with two overloads, a default argument and a member function that calls other member functions:


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

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

	// Declared here, defined outside the class.
	::std::int_least32_t sum() const noexcept;

	// Two overloads, both defined outside.
	void shift_by(::std::int_least32_t dx) noexcept;
	void shift_by(::std::int_least32_t dx, ::std::int_least32_t dy) noexcept;

	// A method that calls other methods.
	::std::int_least32_t doubled_sum() const noexcept;
};

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

void point::shift_by(::std::int_least32_t dx) noexcept
{
	shift_by(dx, 0); // calls the two-argument overload
}

void point::shift_by(::std::int_least32_t dx, ::std::int_least32_t dy) noexcept
{
	x += dx;
	y += dy;
}

::std::int_least32_t point::doubled_sum() const noexcept
{
	return 2 * sum();
}

class counter
{
public:
	::std::int_least32_t value{};

	// Default arguments work exactly as they do for free functions.
	void add(::std::int_least32_t n = 1) noexcept
	{
		value += n;
	}
	void reset() noexcept
	{
		value = 0;
	}
};

int main()
{
	point p{3, 4};
	println("sum         = ", p.sum());
	println("doubled_sum = ", p.doubled_sum());

	p.shift_by(10);
	println("after shift_by(10): ", p.x, ", ", p.y);

	p.shift_by(1, 2);
	println("after shift_by(1, 2): ", p.x, ", ", p.y);

	counter c{};
	c.add();
	c.add(10);
	println("counter = ", c.value);
	c.reset();
	println("counter = ", c.value);
}

sum         = 7
doubled_sum = 14
after shift_by(10): 13, 4
after shift_by(1, 2): 14, 6
counter = 11
counter = 0

Three things to take from it:

3. Member Functions Are Declared noexcept or throws

A member function is a function, so it gets the same treatment as any other function in this tutorial: it is either noexcept (it cannot fail) or throws (it can report a failure to its caller) (Ch6.16). Printing is I/O, and I/O can fail, so a member function that prints is throws:


class point
{
public:
	void print() const throws          // correct: it does I/O
	{
		println("(", x, ", ", y, ")");
	}
};

If you mark it noexcept instead, the compiler stops you at the println line, and tells you exactly why:


error: call to a 'throws' or 'return_failure{E}' function must be handled by an
       enclosing 'try { } catch throws', or the calling function needs a
       herbceptions specification so the error can propagate

The wording is the same as for a free function, because the rule is the same rule. That is the useful part: there is one error-handling concept in the language, and it does not change shape when you walk through a class definition.

Where do definitions go? If a class is used from several .cc files, the class definition goes in a header and every member function defined outside the class must be inline (or be defined in exactly one .cc file). Member functions defined inside the class body are inline automatically. This tutorial keeps each example in one file, so nothing has to be inline.

4. const Appeared

Most of the declarations in the example end with const: sum() const, doubled_sum() const. That word is a promise that the member function will not change the object, and it is not optional decoration — it decides whether you can call the function on a const object at all. It has its own subchapter, because it is the single most important habit in this chapter: Ch13.3.