Объявление класса
Классы это определяемые пользователем типы, определяемые спецификатором класса, который появляется в последовательности-спецификаторов-объявления синтаксиса объявления.
Синтаксис
Спецификатор класса имеет следующий синтаксис:
ключевое-слово-класса атрибуты (необязательно) имя-заголовка-класса final(необязательно) предложение-базы (необязательно) { спецификация-элемента }
|
(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;
}
Спецификация элемента
Спецификация элемента или тело определения класса представляет собой заключённую в фигурные скобки последовательность любого числа из следующего:
атрибуты (необязательно) последовательность-спецификаторов-объявления (необязательно) список-деклараторов-элементов (необязательно) ;
|
|||||||||
| атрибуты | — | (начиная с 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;
};
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];
}
};
public:, protected: и private:
class S
{
public:
S(); // открытый конструктор
S(const S&); // открытый конструктор копирования
virtual ~S(); // открытый виртуальный деструктор
private:
int* ptr; // закрытый элемент данных
};
class Base
{
protected:
int d;
};
class Derived : public Base
{
public:
using Base::d; // делает защищённый элемент базового класса d
// открытым элементом производного
using Base::Base; // наследует конструкторы всех базовых классов (C++11)
};
static_assert:
template<typename T>
struct Foo
{
static_assert(std::is_floating_point<T>::value, "Foo<T>: T должен быть с"
" плавающей запятой");
};
struct S
{
template<typename T>
void f(T&& n);
template<class CharT>
struct NestedS
{
std::basic_string<CharT> s;
};
};
| (начиная с 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 по Объявление структуры
|