Объявление класса
Классы — это определяемые пользователем типы, задаваемые спецификатором класса, который появляется в decl-specifier-seq синтаксиса объявления.
Синтаксис
Спецификатор класса имеет следующий синтаксис:
class-key attr (optional) class-head-name final(optional) base-clause (optional) { member-specification } | (1) | |
class-key attr (optional) base-clause (optional) { member-specification } | (2) |
| class-key | - | одно из ключевых слов class, struct и union. Ключевые слова class и struct идентичны, за исключением доступа к членам и доступа к базовому классу по умолчанию. Если это union, объявление вводит тип объединения. |
| attr | - |
(начиная с C++11) любое количество атрибутов; может включать спецификатор alignas |
| class-head-name | - | имя определяемого класса, при необходимости квалифицированное |
final | - | (начиная с C++11) если присутствует, от этого класса нельзя наследоваться |
| base-clause | - | список из одного или нескольких базовых классов и модели наследования, используемой для каждого из них (см. производный класс) |
| member-specification | - | список спецификаторов доступа, объявлений и определений объектов-членов и функций-членов (см. ниже) |
Предварительное объявление
Объявление следующей формы
class-key attr identifier ; |
объявляет тип класса, который будет определён позже в этой области видимости. До появления определения имя этого класса имеет неполный тип. Это позволяет классам ссылаться друг на друга:
class Vector; // forward declaration
class Matrix
{
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
class Vector
{
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
а если конкретный исходный файл использует только указатели и ссылки на класс, это позволяет сократить число зависимостей от #include:
// In MyStruct.h
#include <iosfwd> // contains forward declaration of std::ostream
struct MyStruct
{
int value;
friend std::ostream& operator<<(std::ostream& os, const S& s);
// definition provided in MyStruct.cpp file which uses #include <ostream>
};
Если предварительное объявление появляется в локальной области видимости, оно скрывает ранее объявленные класс, переменную, функцию и все остальные объявления с тем же именем, которые могут находиться во внешних областях видимости:
struct s { int a; };
struct s; // does nothing (s already defined in this scope)
void g()
{
struct s; // forward declaration of a new, local struct "s"
// this hides global struct s until the end of this block
s* p; // pointer to local struct s
struct s { char* p; }; // definitions of the local struct s
}
Обратите внимание, что новое имя класса также может быть введено уточнённым спецификатором типа, который является частью другого объявления, но только если поиск имени не может найти ранее объявленный класс с тем же именем.
class U;
namespace ns
{
class Y f(class T p); // declares function ns::f and declares ns::T and ns::Y
class U f(); // U refers to ::U
// can use pointers and references to T and Y
Y* p;
T* q;
}
Описание членов
Описание членов, или тело определения класса, представляет собой заключённую в фигурные скобки последовательность, состоящую из любого количества следующих элементов:
attr (optional) decl-specifier-seq (optional) member-declarator-list (optional) ; |
| attr | - | (начиная с C++11) любое количество атрибутов |
| decl-specifier-seq | - | последовательность спецификаторов. Она может отсутствовать только в объявлениях конструкторов, деструкторов и определённых пользователем функций преобразования типов |
| member-declarator-list | - | аналогичен init-declarator-list, но дополнительно допускает объявление битового поля, pure-specifier и virt-specifier (override или final)(начиная с C++11), но не допускает синтаксис прямой инициализации без списка. |
Это объявление может объявлять статические и нестатические данные-члены и функции-члены, typedef-объявления членов, перечисления членов и вложенные классы. Оно также может быть объявлением friend.
class S
{
int d1; // non-static data member
int a[10] = {1, 2}; // non-static data member with initializer (C++11)
static const int d2 = 1; // static data member with initializer
virtual void f1(int) = 0; // pure virtual member function
std::string d3, *d4, f2(int); // two data members and a member function
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) {} // constructor definition
int operator()(std::size_t r, std::size_t c) const // member function definition
{
return data[r * C + c];
}
int& operator()(std::size_t r, std::size_t c) // another member function definition
{
return data[r * C + c];
}
};
public:, protected: и private: class S
{
public:
S(); // public constructor
S(const S&); // public copy constructor
virtual ~S(); // public virtual destructor
private:
int* ptr; // private data member
};
class Base
{
protected:
int d;
};
class Derived : public Base
{
public:
using Base::d; // make Base's protected member d a public member of Derived
using Base::Base; // inherit all bases' constructors (C++11)
};
static_assert: template<typename T>
struct Foo
{
static_assert(std::is_floating_point<T>::value, "Foo<T>: T must be floating point");
};
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) не могут иметь шаблоны-члены.
- Локальные классы не могут иметь шаблоны friend.
- Локальные классы не могут определять функции friend внутри определения класса.
- Локальный класс внутри функции (включая функцию-член) может обращаться к тем же именам, что и охватывающая его функция.
| (до C++11) |
#include <algorithm>
#include <iostream>
#include <vector>
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()); // since C++11
for (int n : v)
std::cout << n << ' ';
std::cout << '\n';
}
Вывод:
3 2 1
Ключевые слова
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение согласно опубликованной версии | Исправленное поведение |
|---|---|---|---|
| CWG 1693 | C++98 | объявления членов не могли быть пустыми | пустое объявление разрешено |
| CWG 1930 | C++98 |
member-declarator-list мог быть пустым, если decl-specifier-seq содержал спецификатор класса хранения или cv-квалификатор | список не должен быть пустым |
| CWG 2890 | C++98 | было неясно, где можно объявлять члены вложенных классов | уточнено |
См. также
Документация по C для Struct declaration |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/class