Нестатические функции-члены
Нестатическая функция-член — это функция, объявленная в спецификации членов класса без спецификатора 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-квалификаторы, ref-квалификаторы, спецификаторы 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 или их сочетанием); эта последовательность указывается после списка параметров в объявлении функции. Функции с различными последовательностями 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
}
Функции-члены с ref-квалификаторомФункцию-член с неявным объектом можно объявить без ref-квалификатора, с ref-квалификатором для 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-квалификации, ref-квалификация не меняет свойства указателя | (начиная с C++11) |
Виртуальные и чистые виртуальные функции
Нестатическую функцию-член можно объявить виртуальной или чистой виртуальной. Подробности см. в разделах виртуальные функции и абстрактные классы.
Функции-члены с явным объектомПервый параметр функции-члена может быть явным параметром объекта (обозначается ключевым словом 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 и без них. Пример см. в разделе оператор индексации массива. Внутри тела функции-члена с явным объектом нельзя использовать указатель 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) и операторами постфиксного инкремента и декремента(начиная с C++29) — единственные функции, которые можно объявить заданными по умолчанию, то есть определить с помощью =default вместо тела функции (подробности см. на соответствующих страницах).
Примечания
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_ref_qualifiers | 200710L | (C++11) | ref-квалификаторы |
__cpp_explicit_this_parameter | 202110L | (C++23) |
явный параметр объекта (вывод this) |
Пример
#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/cpp/language/member_functions