Объявление класса
Классы — это определяемые пользователем типы, определённые спецификатором класса, который появляется в decl-specifier-seq синтаксиса объявления.
Синтаксис
Спецификатор класса имеет следующий синтаксис:
class-key attr (необязательно) class-head-name final(необязательно) base-clause (необязательно) { member-specification } | (1) | |
class-key attr (необязательно) base-clause (необязательно) { 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 (необязательно) decl-specifier-seq (необязательно) member-declarator-list (необязательно) ; |
| attr | - | (с C++11) любое количество атрибутов |
| decl-specifier-seq | - | последовательность спецификаторов. Она необязательна только в объявлениях конструкторов, деструкторов и пользовательских функций преобразования типов преобразования |
| member-declarator-list | - | подобно init-declarator-list, но дополнительно допускает объявление битового поля, чистую спецификацию, и virt-specifier (override или final)(с C++11), и не допускает синтаксис прямого инициализации без списка. |
Это объявление может объявлять статические и нестатические члены данных и члены-функции, typedef членов, перечисления членов и вложенные классы. Это также может быть объявление друга.
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) не могут иметь шаблонов членов.
- Локальные классы не могут иметь шаблонов-друзей.
- Локальные классы не могут определять функции-друга внутри определения класса.
- Локальный класс внутри функции (включая член-функцию) может получить доступ к тем же именам, что и окружающая функция.
| (до 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 |
список-деклараторов-членов мог быть пустым, когда последовательность-спецификаторов-деклараций содержала спецификатор класса хранения или квалификатор cv | список не должен быть пустым |
См. также
Документация C для Struct declaration |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/class