Нестатические члены-функции
Нестатическая функция-член — это функция, объявленная в определении класса без спецификатора static или friend (см. статические функции-члены и объявление друга для понимания влияния этих ключевых слов).
class S
{
int mf1(); // non-static member function declaration
void mf2() volatile, mf3() &&; // can have cv-qualifiers and/or a reference-qualifier
// the declaration above is equivalent to two separate declarations:
// void mf2() volatile;
// void mf3() &&;
int mf4() const { return data; } // can be defined inline
virtual void mf5() final; // can be virtual, can use final/override
S() : data(12) {} // constructors are member functions too
int data;
};
int S::mf1() { return 7; } // if not defined inline, has to be defined at namespaceКонструкторы, деструкторы и функции преобразования используют специальный синтаксис для своего объявления. Правила, описанные на этой странице, могут не применяться к этим функциям. Для получения подробной информации см. соответствующие страницы.
| Явная функция-член объекта — это нестатическая функция-член с явным параметром объекта. | (с C++23) |
Неявная функция-член объекта — это нестатическая функция-член без явного параметра объекта (до C++23 это был единственный тип нестатической функции-члена, и поэтому в литературе она называлась «нестатической функцией-членом»).
Объяснение
Разрешены любые объявления функций, с дополнительными элементами синтаксиса, доступными только для нестатических функций-членов: чистые спецификаторы, cv-квалификаторы, квалификаторы ссылок, final и override спецификаторы(с C++11) и списки инициализации членов.
Нестатическая функция-член класса X может вызываться
X с использованием оператора доступа к членам классаX
X
Вызов нестатической функции-члена класса X для объекта, не являющегося типом X, или типом, производным от X, приводит к неопределенному поведению.
В теле нестатической функции-члена класса X, любое идентификационное выражение e (например, идентификатор), которое разрешается до нетипового нестатического члена класса X или базового класса X, преобразуется в выражение доступа к члену (*this).e (если это не выражение доступа к члену уже). Это не происходит в контексте шаблона определения, поэтому имя может быть предварено this-> явно, чтобы стать зависимым.
struct S
{
int n;
void f();
};
void S::f()
{
n = 1; // transformed to (*this).n = 1;
}
int main()
{
S s1, s2;
s1.f(); // changes s1.n
}В теле нестатической функции-члена класса X, любой неквалифицированный идентификатор, разрешаемый до статического члена, перечислителя или вложенного типа X или базового класса X, преобразуется в соответствующее квалифицированное имя:
struct S
{
static int n;
void f();
};
void S::f()
{
n = 1; // transformed to S::n = 1;
}
int main()
{
S s1, s2;
s1.f(); // changes S::n
}Функции-члены с cv-квалификаторами
Неявную функцию-член объекта можно объявить с последовательностью cv-квалификаторов (const, volatile или комбинацией const и volatile), которая появляется после списка параметров в объявлении функции. Функции с различными последовательностями cv-квалификаторов (или без них) имеют разные типы и могут перегружать друг друга.
В теле функции с последовательностью cv-квалификаторов, *this является cv-квалифицированным, например, в функции-члене с квалификатором const, могут вызываться только другие функции-члены с квалификатором const. Функция-член без квалификатора const всё ещё может быть вызвана, если применяется const_cast или через путь доступа, не включающий this.
#include <vector>
struct Array
{
std::vector<int> data;
Array(int sz) : data(sz) {}
// const member function
int operator[](int idx) const
{ // the this pointer has type const Array*
return data[idx]; // transformed to (*this).data[idx];
}
// non-const member function
int& operator[](int idx)
{ // the this pointer has type Array*
return data[idx]; // transformed to (*this).data[idx]
}
};
int main()
{
Array a(10);
a[1] = 1; // OK: the type of a[1] is int&
const Array ca(10);
ca[1] = 2; // Error: the type of ca[1] is int
} Функции-члены с квалификатором ссылокНеявную функцию-член объекта можно объявить без квалификатора ссылок, с квалификатором ссылки на lvalue (токен
#include <iostream>
struct S
{
void f() & { std::cout << "lvalue\n"; }
void f() && { std::cout << "rvalue\n"; }
};
int main()
{
S s;
s.f(); // prints "lvalue"
std::move(s).f(); // prints "rvalue"
S().f(); // prints "rvalue"
}Примечание: в отличие от cv-квалификации, квалификация ссылками не изменяет свойства указателя | (с C++11) |
Виртуальные и чисто виртуальные функции
Нестатическая функция-член может быть объявлена как виртуальная или чисто виртуальная. Для получения подробной информации см. виртуальные функции и абстрактные классы.
Явный параметр объектаНестатическая функция-член может быть объявлена с первым параметром — явным параметром объекта, обозначенным префиксом ключевым словом struct X
{
void foo(this X const& self, int i); // same as void foo(int i) const &;
// void foo(int i) const &; // Error: already declared
void bar(this X self, int i); // pass object by value: makes a copy of `*this`
};Для функций-членов-шаблонов явный параметр объекта позволяет вывести тип и категорию значения; эта языковая функция называется «вывод struct X
{
template<typename Self>
void foo(this Self&&, int);
};
struct D : X {};
void ex(X& x, D& d)
{
x.foo(1); // Self = X&
move(x).foo(2); // Self = X
d.foo(3); // Self = D&
}Это позволяет избежать дублирования const- и non-const функций-членов, см. оператор индексирования массива для примера. Кроме того, явный параметр объекта выводится до производного типа, что упрощает CRTP: // a CRTP trait
struct add_postfix_increment
{
template<typename Self>
auto operator++(this Self&& self, int)
{
auto tmp = self; // Self deduces to "some_type"
++self;
return tmp;
}
};
struct some_type : add_postfix_increment
{
some_type& operator++() { ... }
};В теле функции с явным параметром объекта указатель struct C
{
void bar();
void foo(this C c)
{
auto x = this; // error: no this
bar(); // error: no implicit this->
c.bar(); // ok
}
};Указатель на функцию-член с явным параметром объекта — обычный указатель на функцию, а не указатель на член: struct Y
{
int f(int, int) const&;
int g(this Y const&, int, int);
};
auto pf = &Y::f;
pf(y, 1, 2); // error: pointers to member functions are not callable
(y.*pf)(1, 2); // ok
std::invoke(pf, y, 1, 2); // ok
auto pg = &Y::g;
pg(y, 3, 4); // ok
(y.*pg)(3, 4); // error: pg is not a pointer to member function
std::invoke(pg, y, 3, 4); // ok
| (с C++23) |
Специальные функции-члены
Некоторые функции-члены являются специальными: в определённых обстоятельствах они определяются компилятором, даже если не определены пользователем. К ним относятся:
| (с C++11) |
| (с C++11) |
- Деструктор(до C++20)Прогнозируемый деструктор(с C++20)
Специальные функции-члены вместе с операторами сравнения(с C++20) — единственные функции, которые могут быть по умолчанию, то есть определённые с помощью = default вместо тела функции (см. их страницы для получения подробной информации).
Примечания
| Макрос проверки функций | Значение | Std | Функция |
|---|---|---|---|
__cpp_ref_qualifiers | 200710L | (C++11) | квалификаторы ссылок |
__cpp_explicit_this_parameter | 202110L | (C++23) | Явный параметр объекта |
Пример
#include <exception>
#include <iostream>
#include <string>
#include <utility>
struct S
{
int data;
// simple converting constructor (declaration)
S(int val);
// simple explicit constructor (declaration)
explicit S(std::string str);
// const member function (definition)
virtual int getData() const { return data; }
};
// definition of the constructor
S::S(int val) : data(val)
{
std::cout << "ctor1 called, data = " << data << '\n';
}
// this constructor has a catch clause
S::S(std::string str) try : data(std::stoi(str))
{
std::cout << "ctor2 called, data = " << data << '\n';
}
catch(const std::exception&)
{
std::cout << "ctor2 failed, string was '" << str << "'\n";
throw; // ctor's catch clause should always rethrow
}
struct D : S
{
int data2;
// constructor with a default argument
D(int v1, int v2 = 11) : S(v1), data2(v2) {}
// virtual member function
int getData() const override { return data * data2; }
// lvalue-only assignment operator
D& operator=(D other) &
{
std::swap(other.data, data);
std::swap(other.data2, data2);
return *this;
}
};
int main()
{
D d1 = 1;
S s2("2");
try
{
S s3("not a number");
}
catch(const std::exception&) {}
std::cout << s2.getData() << '\n';
D d2(3, 4);
d2 = d1; // OK: assignment to lvalue
// D(5) = d1; // ERROR: no suitable overload of operator=
}Вывод:
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| Отчёт об ошибке | Применено к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 194 | C++98 | неопределённо, может ли нестатический член-функция иметь то же имя, что и имя окружающего класса | добавленное явное ограничение именования |
См. также
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/member_functions