Non-static member functions
Нестатическая функция-член — это функция, объявленная в
спецификации класса
без использования спецификаторов
static
или
friend
(см.
статические функции-члены
и
объявление дружественной функции
для понимания эффекта этих ключевых слов).
class S { int mf1(); // объявление нестатической функции-члена void mf2() volatile, mf3() &&; // может иметь cv-квалификаторы и/или ссылочный квалификатор // объявление выше эквивалентно двум отдельным объявлениям: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // может быть определено внутри класса virtual void mf5() final; // может быть виртуальной, может использовать final/override S() : data(12) {} // конструкторы также являются функциями-членами int data; }; int S::mf1() { return 7; } // если не определено внутри класса, должно быть определено в пространстве имен
Конструкторы , деструкторы , и функции преобразования используют специальный синтаксис для своих объявлений. Правила, описанные на этой странице, могут не применяться к этим функциям. Подробности смотрите на соответствующих страницах.
|
Явная объектная функция-член — это нестатическая функция-член с явным объектным параметром . |
(since C++23) |
Неявная функция-член объекта — это нестатическая функция-член без явного параметра объекта (до C++23 это был единственный вид нестатических функций-членов, поэтому в литературе они назывались «нестатическими функциями-членами»).
Содержание |
Объяснение
Любые
объявления функций
разрешены, с дополнительными синтаксическими элементами, доступными только для нестатических функций-членов:
чистые спецификаторы
, cv-квалификаторы
, ref-квалификаторы,
final
и
override
спецификаторы
(начиная с C++11)
, и
списки инициализации членов
.
Нестатическая функция-член класса
X
может быть вызвана
X
с использованием оператора доступа к члену класса
X
X
Вызов нестатической функции-члена класса
X
для объекта, который не является типом
X
или типом, производным от
X
, вызывает неопределённое поведение.
В теле нестатической функции-члена класса
X
любое
id-выражение
e
(например, идентификатор), которое разрешается в не-типовой нестатический член
X
или базового класса
X
, преобразуется в выражение доступа к члену
(
*
this
)
.
e
(если оно уже не является частью выражения доступа к члену). Это не происходит в контексте определения шаблона, поэтому имя может потребоваться явно предварить
this
-
>
, чтобы стать
зависимым
.
struct S { int n; void f(); }; void S::f() { n = 1; // преобразуется в (*this).n = 1; } int main() { S s1, s2; s1.f(); // изменяет s1.n }
В теле нестатической функции-члена класса
X
любой неквалифицированный идентификатор, который разрешается в статический член, перечислитель или вложенный тип класса
X
или базового класса
X
, преобразуется в соответствующий квалифицированный идентификатор:
struct S { static int n; void f(); }; void S::f() { n = 1; // преобразуется в S::n = 1; } int main() { S s1, s2; s1.f(); // изменяет 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) {} // константная функция-член int operator[](int idx) const { // указатель this имеет тип const Array* return data[idx]; // преобразуется в (*this).data[idx]; } // неконстантная функция-член int& operator[](int idx) { // указатель this имеет тип Array* return data[idx]; // преобразуется в (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK: тип a[1] - int& const Array ca(10); ca[1] = 2; // Ошибка: тип ca[1] - int }
Функции-члены с ref-квалификатором
Неявная функция-член объекта может быть объявлена без ref-квалификатора, с lvalue ref-квалификатором (токен
Примечание: в отличие от cv-квалификации, ref-квалификация не изменяет свойства указателя
|
(начиная с C++11) |
Виртуальные и чисто виртуальные функции
Нестатическая функция-член может быть объявлена виртуальной или чисто виртуальной . Подробности смотрите в разделах виртуальные функции и абстрактные классы .
Явные объектные функции-членыДля нестатической невиртуальной функции-члена, не объявленной с cv-квалификатором или ref-квалификатором, её первый параметр, если он не является пакетом параметров функции , может быть явным объектным параметром (обозначается с помощью префиксного ключевого слова this ): 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” }; Для шаблонов функций-членов явный объектный параметр позволяет выводить тип и категорию значения, эта языковая возможность называется «вывод 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- и не-const функций-членов, см. оператор индексации массива для примера. В теле явной объектной функции-члена указатель this не может использоваться: все обращения к членам должны выполняться через первый параметр, как в статических функциях-членах: 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 вместо тела функции (подробности см. на соответствующих страницах).
Примечания
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_ref_qualifiers
|
200710L
|
(C++11) | ref-qualifiers |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) |
explicit object parameter
(
deducing
this
)
|
Пример
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // простой преобразующий конструктор (объявление) S(int val); // простой явный конструктор (объявление) explicit S(std::string str); // константная функция-член (определение) virtual int getData() const { return data; } }; // определение конструктора S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // этот конструктор имеет блок catch 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; // блок catch конструктора всегда должен повторно выбрасывать исключение } struct D : S { int data2; // конструктор с аргументом по умолчанию D(int v1, int v2 = 11) : S(v1), data2(v2) {} // виртуальная функция-член int getData() const override { return data * data2; } // оператор присваивания только для lvalue 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: присваивание lvalue // D(5) = d1; // ОШИБКА: нет подходящей перегрузки operator= }
Вывод:
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 194 | C++98 |
неясно, может ли нестатическая функция-член
иметь то же имя, что и имя объемлющего класса |
добавлено явное ограничение на именование |