Пространства имён
Варианты
Действия

Объявление класса

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

Классы это определяемые пользователем типы, определяемые спецификатором класса, который появляется в последовательности-спецификаторов-объявления синтаксиса объявления.

Содержание

[править] Синтаксис

Спецификатор класса имеет следующий синтаксис:

ключевое-слово-класса атрибуты (необязательно) имя-заголовка-класса final(необязательно) предложение-базы (необязательно) { спецификация-элемента } (1)
ключевое-слово-класса атрибуты (необязательно) предложение-базы (необязательно) { спецификация-элемента } (2)
1) определение именованного класса
2) определение безымянного класса
ключевое-слово-класса один из class, struct и union. Ключевые слова class и struct идентичны, за исключением значения по умолчанию доступа к элементам и значения по умолчанию доступа к базовому классу. Если это union, объявление вводит тип объединения.
атрибуты (начиная с C++11) любое количество атрибутов, может включать спецификатор alignas
имя-заголовка-класса имя определяемого класса, необязательно квалифицированное
final (начиная с C++11) если присут��твует, класс не может наследоваться
предложение-базы список из одного или нескольких базовых классов и модель наследования, используемая для каждого из них (смотрите производный класс)
спецификация-элемента список спецификаторов доступа, объявлений и определений объектов-элементов и функций-элементов (смотрите ниже).

[править] Предварительное объявление

Объявление следующей формы

ключевое-слово-класса атрибуты идентификатор ;

Объявляет тип класса, который будет определён позже в этой области. Пока не появится определение, это имя класса имеет неполный тип. Это позволяет классам ссылаться друг на друга:

class Vector; // предварительное объявление
 
class Matrix
{
    // ...
    friend Vector operator*(const Matrix&, const Vector&);
};
 
class Vector
{
    // ...
    friend Vector operator*(const Matrix&, const Vector&);
};

и если конкретный исходный файл использует только указатели и ссылки на класс, это позволяет уменьшить #include зависимости:

// в MyStruct.h 
#include <iosfwd> // содержит предварительное объявление std::ostream
 
struct MyStruct
{
    int value;
    friend std::ostream& operator<<(std::ostream& os, const S& s);
    // определение предоставлено в файле MyStruct.cpp, который использует
    // #include <ostream>
};

Если предварительное объявление появляется в локальной области видимости, оно скрывает ранее объявленный класс, переменную, функцию и все другие объявления с тем же именем, которые могут появляться в окружающих областях видимости:

struct s { int a; };
struct s; // ничего не делает (s уже определена в этой области видимости)
 
void g()
{
    struct s; // предварительное объявление новой локальной структуры "s"
              // скрывает глобальные структуры до конца этого блока видимости
 
    s* p;     // указатель на локальную структуру s
 
    struct s { char* p; }; // определения локальной структуры s
}

Обратите внимание, что новое имя класса также может быть введено уточнённым спецификатором типа, который появляется как часть другого объявления, но только в том случае, если поиск по имени не может найти ранее объявленный класс с таким же именем.

class U;
 
namespace ns
{
    class Y f(class T p); // объявляет функцию ns::f и объявляет ns::T и ns::Y
 
    class U f();          // U ссылается на ::U
 
    // можно использовать указатели и ссылки на T и Y
    Y* p;
    T* q;
}

[править] Спецификация элемента

Спецификация элемента или тело определения класса представляет собой заключённую в фигурные скобки последовательность любого числа из следующего:

1) Объявления элементов в форме
атрибуты (необязательно) последовательность-спецификаторов-объявления (необязательно) список-деклараторов-элементов (необязательно) ;
атрибуты (начиная с C++11) любое количество атрибутов
последовательность-спецификаторов-объявления последовательность спецификаторов. Она необязательна только в объявлениях конструкторов, деструкторов и определяемых пользователем функций преобразования типов
список-деклараторов-элементов подобно списку-деклараторов-инициализации, но дополнительно позволяет объявление битового поля, чистый-спецификатор и виртуальный спецификатор (override или final) (начиная с C++11), и не позволяет синтаксис прямой инициализации без списка.

Это объявление может объявлять статические и нестатические элементы данных и функции-элементы, элементы typedef, элементы перечислений и вложенные классы. Оно также может быть дружественными объявлениями.

class S
{
    int d1;            // нестатический элемент данных
    int a[10] = {1,2}; // нестатический элемент данных с инициализатором (C++11)
 
    static const int d2 = 1; // статический элемент данных с инициализатором
 
    virtual void f1(int) = 0; // чистая виртуальная функция-элемент
 
    std::string d3, *d4, f2(int); // два элемента данных и функция-элемент
 
    enum {NORTH, SOUTH, EAST, WEST};
 
    struct NestedS
    {
        std::string s;
    } d5, *d6;
 
    typedef NestedS value_type, *pointer_type;
};
2) Определения функций, которые объявляют и определяют функции-элементы или дружественные функции. Точка с запятой после определения функции-элемента необязательна. Все функции, определённые внутри тела класса, автоматически становятся inline, если только они не присоединены к именованному модулю (начиная с C++20).
class M
{
    std::size_t C;
    std::vector<int> data;
public:
    M(std::size_t R, std::size_t C) : C(C), data(R*C) {} // определение конструктора
 
    int operator()(std::size_t r, std::size_t c) const // определение функции-элемента
    {
        return data[r * C + c];
    }
 
    int& operator()(std::size_t r, std::size_t c)      // другое определение функции-элемента
    {  
        return data[r * C + c];
    }
};
3) Спецификаторы доступа public:, protected: и private:
class S
{
public:
    S();          // открытый конструктор
    S(const S&);  // открытый конструктор копирования
    virtual ~S(); // открытый виртуальный деструктор
private:
    int* ptr; // закрытый элемент данных
};
4) Using-объявления:
class Base
{
protected:
    int d;
};
 
class Derived : public Base
{
public:
    using Base::d; // делает защищённый элемент базового класса d
                   // открытым элементом производного
    using Base::Base; // наследует конструкторы всех базовых классов (C++11)
};
5) Объявление static_assert:
template<typename T>
struct Foo
{
    static_assert(std::is_floating_point<T>::value, "Foo<T>: T должен быть с"
                                                    " плавающей запятой");
};
6) объявления шаблонов элементов:
struct S
{
    template<typename T>
    void f(T&& n);
 
    template<class CharT>
    struct NestedS
    {
        std::basic_string<CharT> s;
    };
};
7) объявления псевдонимов:
template<typename T>
struct identity
{
    using type = T;
};
(начиная с C++11)
8) руководства по выводу шаблонов элементов классов:
struct S
{
    template<class CharT>
    struct NestedS
    {
        std::basic_string<CharT> s;
    };
 
    template<class CharT>
    NestedS(std::basic_string<CharT>) -> NestedS<CharT>;
};
(начиная с C++17)
9) Объявления using enum:
enum class color { red, orange, yellow };
 
struct highlight
{
    using enum color;
};
(начиная с C++20)

[править] Локальные классы

Объявление класса может появиться внутри тела функции, и в этом случае оно определяет локальный класс. Имя такого класса существует только внутри области действия функции и недоступно из вне.

  • Локальный класс не может иметь статических элементов данных
  • Функции-элементы локального класса не имеют связывания
  • Функции-элементы локального класса должны быть определены полностью внутри тела класса
  • Локальные классы , отличные от типов замыкания, (начиная с C++14) не могут иметь шаблонных элементов
  • Локальные классы не могут иметь дружественные шаблоны
  • Локальные классы не могут определять дружественные функции внутри определения класса
  • Локальный класс внутри функции (включая функцию-элемент) может обращаться к тем же именам, к которым может обращаться включающая функция.
  • локальные классы нельзя было использовать в качестве аргументов шаблона
(до C++11)
#include <vector>
#include <algorithm>
#include <iostream>
 
int main()
{
    std::vector<int> v{1, 2, 3};
 
    struct Local
    {
        bool operator()(int n, int m)
        {
            return n > m;
        }
    };
 
    std::sort(v.begin(), v.end(), Local()); // начиная с C++11
 
    for (int n : v)
        std::cout << n << ' ';
}

Вывод:

3 2 1

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1693 C++98 объявления элементов не могут быть пустыми пустое объявление разрешено
CWG 1930 C++98 список деклараторов элементов может быть пустым, если
последовательность-спецификаторов-объявления содержит
спецификатор класса хранения или cv квалификатор
список не должен быть пустым

[править] Смотрите также

Документация C по Объявление структуры