Ch13.2: Member Functions
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:
-
Overloading works exactly as for free functions (see the two
shift_bys). The compiler picks by the arguments you pass, on top of the object it already knows. -
Default arguments work too (
add(n = 1)). As always, the default belongs on the declaration the caller sees. -
Member functions call each other by name
(
shift_by(dx, 0),sum()). No object has to be written, because there is only one sensible object: the one the caller is already holding.
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.
.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.