Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 2 of 12

A member function is a function that belongs to a type and can see its data without being handed it.

#include <iostream>

class Counter {
    int count = 0;              // default member initialiser

public:
    void increment() { ++count; }          // sees `count` directly
    void reset() { count = 0; }
    int value() const { return count; }
};

int main() {
    Counter c;
    c.increment();
    c.increment();
    c.increment();
    std::cout << c.value() << "\n";

    c.reset();
    std::cout << c.value() << "\n";
    return 0;
}
3
0

increment() never receives count as a parameter. Every member function has an implicit reference to the object it was called on, which lesson 9.5 makes explicit as this.

Default member initialisers

int count = 0; inside the class gives every Counter a starting value without writing a constructor. Prefer this: an uninitialised member is the class-shaped version of lesson 3.1's garbage variable.

#include <iostream>
#include <string>

struct Config {
    std::string host = "localhost";
    int port = 8080;
    bool verbose = false;
};

int main() {
    Config c;
    std::cout << c.host << ":" << c.port << " verbose=" << c.verbose << "\n";

    Config custom;
    custom.port = 9090;
    std::cout << custom.host << ":" << custom.port << "\n";
    return 0;
}
localhost:8080 verbose=0
localhost:9090

const member functions

A member function marked const promises not to modify the object:

#include <iostream>

class Temperature {
    double celsius = 0.0;

public:
    void set(double c) { celsius = c; }              // modifies: not const

    double get() const { return celsius; }           // reads only: const
    double fahrenheit() const { return celsius * 9.0 / 5.0 + 32.0; }

    // Trying to write to a member inside a const function is an error:
    //   double bad() const { celsius = 1.0; return celsius; }
    //   error: assignment of member 'Temperature::celsius' in read-only object
};

int main() {
    Temperature t;
    t.set(100.0);
    std::cout << t.get() << "C = " << t.fahrenheit() << "F\n";
    return 0;
}
100C = 212F

The const goes after the parameter list, which is unusual-looking but consistent: it applies to the implicit object parameter, not to the return value.

Why const matters more than it looks

A const object can only call const member functions:

#include <iostream>

class Box {
    int value = 7;

public:
    int get() const { return value; }
    void set(int v) { value = v; }
};

void inspect(const Box& b) {
    std::cout << b.get() << "\n";       // fine: get() is const
    // b.set(1);                        // error: passing 'const Box' as 'this'
                                        //        discards qualifiers
}

int main() {
    Box b;
    inspect(b);

    const Box frozen;
    std::cout << frozen.get() << "\n";  // only const members are callable
    return 0;
}
7
7

This is why forgetting const spreads: a member function that could be const but is not cannot be called on a const object, so callers are forced to drop const too, and the guarantee unravels through the codebase.

Mark every member function const that does not modify the object. It costs nothing and is tedious to retrofit.

Defining members outside the class

Long functions clutter a class definition. Define them outside with ClassName:::

#include <iostream>

class Rectangle {
    double width = 0.0;
    double height = 0.0;

public:
    void set_size(double w, double h);
    double area() const;
    double perimeter() const;
};

void Rectangle::set_size(double w, double h) {
    width = (w < 0.0) ? 0.0 : w;
    height = (h < 0.0) ? 0.0 : h;
}

double Rectangle::area() const {
    return width * height;
}

double Rectangle::perimeter() const {
    return 2.0 * (width + height);
}

int main() {
    Rectangle r;
    r.set_size(3.0, 4.0);
    std::cout << r.area() << " " << r.perimeter() << "\n";
    return 0;
}
12 14

Note the const must appear in both the declaration and the definition — they are part of the signature. Omitting it in one place produces a confusing "no declaration matches" error.

This split is what header files are for (lesson 5.8): the class definition goes in the .hpp, the member function bodies in the .cpp.

Members defined inside are implicitly inline

A member function whose body is inside the class body is implicitly inline, which is why you can put it in a header without violating the one-definition rule. A member defined outside the class in a header needs an explicit inline, or it belongs in a .cpp.

Try it

Write a class BankAccount with a private balance, and deposit, withdraw, and balance members. withdraw should refuse to overdraw. Mark everything const that can be, then try calling each member on a const BankAccount&.

Checkpoint

  • A member function belongs to the type and accesses its members without parameters.
  • Default member initialisers (int count = 0;) prevent uninitialised members.
  • const after the parameter list promises the function does not modify the object.
  • A const object can only call const member functions, so missing const spreads.
  • Members can be defined outside the class with Type::name; const must match in both places.
  • Bodies written inside the class are implicitly inline.

Questions about this lesson

Log in to ask a question.