Ch13.9: Static Member Functions

English

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

1. A Member Function Without an Object

A static member function is a member function that is not called on an object. It has no this (Ch13.7), it is called with the class name instead of a variable name, and it can be called even when no object of the class exists:


::std::size_t n{account::opened()};   // no object involved

Everything else is unchanged: it is inside the class, so it obeys the same access rules — it may read and write private members, including the private constructors. That combination, “outside the object, inside the wall”, is what makes it useful.

2. Static Member Functions Cannot Be const

const on a member function is a statement about this, and a static member function has no this, so the two cannot be combined:


error: static member function 'static int_least32_t counter::instances()'
       cannot have cv-qualifier

It also cannot name a non-static data member, for the same reason: there is no object, so “the member of this object” has no meaning. Static members may of course be used — those belong to the class, which is exactly what a static member function is attached to.

3. The Main Use: A Named Constructor

A constructor has one name: the name of the class. If a class can be built in several ways with several different meanings, constructors stop being expressive: account{100} and account{100, 5} do not say what the numbers are. A static member function gives the construction a name, and lets you do work before the object exists:


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

class account
{
public:
	// A named constructor: the only way to make an account.
	static account open(::std::int_least64_t opening) throws
	{
		if (opening < 0)
		{
			throw throws ::std::errc::invalid_argument;
		}
		return account{opening};
	}

	// No object needed: it reports on the class as a whole.
	static ::std::size_t opened() noexcept
	{
		return next_id_ - 1;
	}

	::std::int_least64_t balance() const noexcept
	{
		return balance_;
	}
	::std::size_t id() const noexcept
	{
		return id_;
	}

private:
	explicit account(::std::int_least64_t opening) noexcept
		: balance_{opening}, id_{next_id_++}
	{
	}

	::std::int_least64_t balance_{};
	::std::size_t id_{};
	inline static ::std::size_t next_id_{1};
};

int main()
{
	try
	{
		auto a{account::open(100)};
		auto b{account::open(250)};

		println("a: id = ", a.id(), ", balance = ", a.balance());
		println("b: id = ", b.id(), ", balance = ", b.balance());
		println("accounts opened: ", account::opened());

		auto bad{account::open(-5)};
		println("never reached: ", bad.balance());
	}
	catch throws (::std::error e)
	{
		print("account::open failed: ", e, "\n");
	}
}

a: id = 1, balance = 100
b: id = 2, balance = 250
accounts opened: 2
account::open failed: [posix](22)Invalid argument

4. When to Write One

Use a static member function Use an ordinary member function
The operation is about the class as a whole: how many exist, what the limits are, how to build one from some other representation. The operation is about one object: deposit, withdraw, balance.
You need to validate or transform the arguments before constructing an object. The object already exists and you are changing or reading it.
You must reach private members but have no object to reach them through. You have the object — that is the normal case.
A static member function is a free function with access. If a helper does not need private members and does not conceptually belong to the class, write it as a free function outside the class. That keeps the class small, and it keeps the helper testable on its own. Conversely, if a helper needs to poke at the private data, friend (Ch13.10) is the other way to allow it without making the data public.
A static member function is part of the class’s interface. Once it is after public: it is a name you have promised to keep, exactly like a public member function. Put it there only if callers outside the class really need it; a validation helper that only open uses belongs after private:.