2. Классы и объекты в С . Определение класса с помощью class, struct, union. Ограничение доступа к членам класса в С . Члены класса и объекта. Методы. Константные члены. Схемы наследования. - keykranz/oop_ex GitHub Wiki

# Классы. Определение класса с помощью class, struct, union.

Класс – тип данных, представляющий совокупность атрибутов объекта (переменные члены класса) и возможных операций над этими объектами (методов класса).

Класс можно определить с помощью следующих ключевых слов:

struct - для совместимости с языком Си - уровень доступа public по-умолчанию class - private union - union нужен только для совместимости с языком, т.к. нет контроля со стороны компилятора, его не используем

На основе этого можно организовать класс. Кроме членов данных у них могут быть функции (методы), которые работают с этими данными.

Уровни доступа к членам class

  • private (частичный) - имеют доступ только члены самого класса. При этом производные классы не имеют доступа к членам private базового класса.
  • protected (защищенный) - имеют доступ методы самого класса и методы производных классов
  • public (общий) - к членам класса можно обратиться из любого места в программе

По правилам инкапсуляции, поля должны быть private или protected.

class <имя класса> [:<список базовых классов>]
{
private: //доступ к данным есть только внутри самого класса
	Int a;
protected: //доступ есть внутри класса и во всех его наследниках
	Int b;
public: //доступны для внешнего кода
	Int f();
}; //класс заканчивается ; как и структура

//Классы описываются в заголовочных файлах .h. Методы же определяются в .cpp

MyClass a, *b = &a;
a.f();
b->f();

По умолчанию, все члены (методы и поля) в struct – public, в class – private, а в union нет уровня protected, так как union не может быть ни базовым классом, ни производным. Разница между struct и class заключается только в уровне доступа к полям.

При разработке класса логично располагать данные по уровням: private, protected, public

При разработке библиотечного класса пользователю стоит дать увидеть public члены, остальные его как правило не интересуют

Размещение полей данных класса (атрибутов) публичными – плохой тон, поскольку это противоречит принципу инкапсуляции (скрывание реализации, предоставление пользователю только интерфейса для работы).

class A
{
	private:
		int a; // данные объекта
		const int cb; // константные данные объекта
		static int sc; // общее поле для всех объектов класса, располагает в другой области памяти
		static const int scd = 2; // константное общее поле для всех объектов класса, разумно инициализировать его здесь
// с 11 версии с++ в классе можно инициализировать любой другой член
	public:
		int f(); // метод объекта
// указатель на объект передается неявно
// метод можно объявить внутри класса и вне 
		int f() const; // константный метод объекта
    	// если const слева, то метод возвращал бы константу
		static int g(); // метод класса, не может быть константным, тк не принимает указателя this
};

Объединения и структуры из си являются частными случаями классов.

union IPv4
{
 int IntegerForm;
 char ByteForm[4];
};

Однако юнион нельзя использовать в качестве базового класса и использовать для наследования.

struct Font
{
 int Symbol:8;
 int isItalic:1;
 int isBold:1;
 int color:3
 int :3 //необходимо для «закрытия» куска памяти – чтобы следующий юнион вдруг не попал на разделение байтов
};

Схемы наследования.

Класс может наследоваться от другого класса. Наследование - создание нового класса на основе старого. В C ++ есть 3 схемы наследования:

  • публичный (public)- все открытые члены базового класса становятся открытыми членами производного класса, защищенные члены становятся защищенными членами, закрытые элементы базового класса остаются закрытыми в этом классе и недоступны для членов производного класса. (расширение интерфейса)

публичные (public) и защищенные (protected) данные наследуются без изменения уровня доступа к ним; (доступ к унаследованным данным не меняется)

  • защищенный (protected) — все открытые и защищенные члены базового класса становятся защищенными членами производного класса.

  • приватный (private) — открытые и защищенные члены базового класса становятся закрытыми членами производного класса. Они все еще доступны из членов производного класса, но из других частей программы к ним обратиться нельзя. (происходит полная замена интерфейса)

Члены класса и объекта.

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

Статические члены или методы.

Один общий для всех объектов этого класса. Доступен сразу из класса, без создания объекта. Вызывается для класса. Статические методы не имеют указателя this.

Class A 
{ 
 public: 
  static int a; 
  static void f();
}; 
// static поля класса можно проинициализировать здесь
A::a = 0; 
A::f();

Константные члены.

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

class A {
public:
	const int a;
	void f(); //1
	void f() const; //2
}
A b; b.f() // 1
Const A c; c.f() // 2

Пример:

class A
{
private:
    int a;				      // Поле объекта
    static int sa;			  // Поле класса
    const int ca;			  // Константа объекта
    const static int csa = 0; // Константа класса
    // Проинициализировать поля класса можно здесь
    // (пример с csa выше)
        
public:
    A(int ia) :
    // Объект еще не создан
        a(ia),  // Так можно - объекта еще нет	 
        ca(ia), // Так можно - объекта еще нет
        sa(ia), // Ошибка - sa не член объекта
        csa(ia) // Ошибка - cas не член объекта
    {	
    // Объект создан 
        a = ia;   // Так можно
        ca = ia;  // Константу изменить нельзя - объект уже создан
        sa = ia;  // Так можно	
        csa = ia; // Константу изменить нельзя - объект уже создан
    }
};

// static поля класса можно проинициализировать здесь
int A::sa = 0;
const int A::csa = 0;

Методы

2 группы методов:

  • методы объекта
  • методы класса

Метод класса вызывается имя_класса::имя_метода. Метод объекта вызывается через сам объект. Методы объекта/класса имеют доступ ко всем элементам класса. Методы класса не получают this. Что делать? Передавать элементы через параметры. Методы класса могут работать только со статическими членами.

Если имена методов одинаковые, то в случае если объект не константный, то будет вызываться обычный метод. Если объект константный, то будет вызываться константный метод. Если константного метода нет, к константному методу нельзя вызвать обычный метод.

Если метод определяем внутри класса, то компилятор делает этот метод по возможности inline. Можно определить метод вне класса с модификатором inline (вместо вызова подставляется код). Если метод в одну строчку, то разумно сделать его inline. Методы не располагаются в самом объекте, это функции, которые при вызове неявно получают указатель на объект. :: - оператор доступа контекстум

Пример:

class A:
{
private:
    int a;
    static int sa;
public:
    int f();		// метод объекта (не const)
    int f() const;  // const метод объекта обязуется не менять состояние объекта,
                    // то есть не менять его любые поля
                   
    
    static int g(); // метод класса
};

int A::f() 		    // :: - оператор доступа к контексту (метод класса вне его описания)
{
    return this->a; // this – ключевое слово объекта, его можно не писать
    return a;       // равносильно
}

int A::g()         // Здесь, в статическом методе, можно работать только
{                  // со статическими членами.
    return sa;
}

Другой пример:

int A::f();
{
	this -> a = 2; //	this - обращение к объекту класса
	return a;
}
int i;
{
	int i;
	i = 2; // внутренняя i
	::i = 3; // внешняя i
}
. //вызов метода через объект
-> //вызов метода через указатель
.* // вызов метода через объект и указатель на метод
->* // вызов метода через указатель на объект и указатель на метод
:: // для статических методов
A obj, *pobj;
obj.f();
pobj->f();
A::g()

Способ объявления статической переменной класс до версии С++11: int A::sc = 2;

В самом классе инициализация может быть только константным выражением - они должны вычисляться на этапе компиляции.

Прочее

//Объединения и структуры из си являются частными случаями классов.
union IPv4
{
	int IntegerForm;
	char ByteForm[4];
};

//Однако юнион нельзя использовать в качестве базового класса и использовать для наследования.

struct Font
{
	int Symbol:8;
	int isItalic:1;
	int isBold:1;
	int color:3
		int :3 //необходимо для «закрытия» куска памяти – чтобы следующий юнион вдруг не попал на разделение байтов
};

//Допускается неполное объявление: без определения класса.
class A; 

//Создание объектов проходит как с помощью 
MyClass A; 
//так и с помощью
MyClass *A = new MyClass;

<class.h>
class B
{
private:
	const int a; //поле должно быть инициализировано вместе с экземпляром класса, и не может изменяться.
public:
	B(int a); //конструктор
	void f() const; //константный метод – может вызываться для константных объектов; не может работать с константными полями
	void g();
};

<class.cpp>
#include “class.h”
B::B(int val): a(val) //, c(7) – всё это инициализация
{
}

const B obj; //в константном экземпляре можно вызывать ТОЛЬКО константные методы: obj.f() НО НЕ obj.g()


//Статические члены классов
class A
{
public:
	static void func();
private:
	static int b;
	static const char* text;
};

//Инициализация проходит через
int A::b = 0;
const char* text = "abc";

С ОБЯЗАТЕЛЬНЫМ УКАЗАНИЕМ ТИПА. Без этого компилятор ругнётся на недоступность переменных. Статический метод не привязан к конкретному экземпляру.

По возможности компилятор делает методы класса инлайновыми.

⚠️ **GitHub.com Fallback** ⚠️