Spec-Zone.ru › C++

Нестатические функции-члены

Нестатическая функция-член — это функция, объявленная в спецификации членов класса без спецификатора 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 можно вызвать

1) Для объекта типа X с помощью оператора доступа к члену класса
2) Для объекта класса, производного от X
3) Непосредственно из тела функции-члена класса X
4) Непосредственно из тела функции-члена класса, производного от 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 (токен & после списка параметров) или с ref-квалификатором для rvalue (токен && после списка параметров). При разрешении перегрузки функция-член с неявным объектом и последовательностью cv-квалификаторов класса X рассматривается следующим образом:

  • без ref-квалификатора: неявный параметр объекта имеет тип «ссылка lvalue на cv-квалифицированный X» и, кроме того, может связываться с аргументом объекта rvalue
  • с ref-квалификатором для lvalue: неявный параметр объекта имеет тип «ссылка lvalue на cv-квалифицированный X»
  • с ref-квалификатором для rvalue: неявный параметр объекта имеет тип «ссылка rvalue на cv-квалифицированный X»
#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-квалификация не меняет свойства указателя this: внутри функции с ref-квалификатором для rvalue *this остаётся выражением lvalue.

(начиная с C++11)

Виртуальные и чистые виртуальные функции

Нестатическую функцию-член можно объявить виртуальной или чистой виртуальной. Подробности см. в разделах виртуальные функции и абстрактные классы.

Функции-члены с явным объектом

Первый параметр функции-члена может быть явным параметром объекта (обозначается ключевым словом this перед параметром), за исключением следующих случаев:

  • функция является статической или виртуальной;
  • функция объявлена с cv-квалификатором или ref-квалификатором;
  • первый параметр является пакетом параметров функции.
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 и без них. Пример см. в разделе оператор индексации массива.

Внутри тела функции-члена с явным объектом нельзя использовать указатель 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) и операторами постфиксного инкремента и декремента(начиная с 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API