Try   HackMD

10 лекция - C++

(30.04.2022)

Методы

Методы - функции класса или структуры


struct A
{
	int x;
	int f() {return x;}
};

A a;

sizeof(a);	//4, размеру int, тк функция не храниться в классе, она хранится в секции кода, имеет специльное скрытое имя, для связывания с классом A 

sizeof(A); //A, тоже самое

Классы

Инкапсуляция

Если вы хотите ограничить видимость некоторых методов и данных от пользователя библиотеки (Тк методы и поля могут поменяться в дальшейшем, что не должно ломать код, который раньше использовал вашу библиотеку)

Модификаторы доступа

  1. private
  2. public
  3. protected (про наследование)

struct A
{
private: //(всё до конца закрыто снаружи или другого модификатора)
	int x;
public: //(всё до конца доступно снаружи или другого модификатора)
	int f() {return x;}
};

A a;

У struct по умолчанию модификатор доступа public У class по умолчанию модификатор доступа private Больше различий НЕТ!

Как обратиться к private полям и методам если очень хочется

#define class struct
#define private public

(Можно сделать через ключи компиляции)

Дефайны через ключи компиляции задают define на все файлы, если есть такой-же define в коде он переопредилит значние на новое (два одинаковых define не ошибка, варнинг, новый его переопределит)

Наследование

Когда у нас есть два класа, которые делают похожие вещи, но немного поразному. Надо выделить общее - родительский класс, и наследовать от него разные реализации

protected поля не видны для окружающего мира, но видны наследникам

class A
{
private: 
	int x;
public: 
	int f() {return x;}
protected:
	float x;
};

class B : public A //('public' - способ наследования)
{
private:
	int z;
public:
	int g() {return f() + z;}
	int f() {return A::f() + z;} //можно перекрыть исходную (A::f() - вызов f из A)
protected:

};

B b;
b.f(); //будет вызов из B

способ наследования понижает видимость до той, что мы указали

  1. public ничего не меняет
  2. protected сделает public поля класса A полями типа protected
  3. private сделает public и protected поля класса A полями типа private (к ним будет доступ из B, естественно)
class A
{
private: 
	int y;
public: 
	int f() {return x;}
protected:
	float x;

friend int h(A &a);	//можно обьявить функцию или класс другом, она получает доступ ко всем private полям, Дружественность не наследуется и работает в одну сторону!	
//Не метод класса

	/*static int h() //Как статический метод, только без friend извращений
	{
		return y + 1;
	} //*/
};

int h(A &a)
{
	return a.y + 1;
}

friend можно обьявлять в любом месте класса, и не может быть использован для доступа к private полям родителя.

Конструкторы/деструкторы при наследовании

Любят писать, что конструкторы и деструкторы не наследуются, но не все понимают, что это значит

class A
{
private: 
	int y;
public: 
	int f() {return x;}

	A(int x = 0, int t = 2){}
};

class B : public A
{
private:
	int z;
public:
	int f() {return A::f() + z;} 
protected:

};

A a(1, 4);
//B b(1, 4);	//Так нельзя
B b; //здесь будет вызван конструктор A

Конструкторы наследуются, но не переходят к наследнику

class A
{
private: 
	int y;
public: 
	int f() {return x;}

	A(int x, int t = 2){}
};

class B : public A
{
private:
	int z;
public:
	int f() {return A::f() + z;} 
	B() : A(3) {}	//так можно иниц родителей
protected:

};

A a(1, 4);
//B b(1, 4);	//Так нельзя
B b; //здесь будет вызван конструктор A

Сначала вызовется деструктор B, затем он вызывает деструктор A.

Указатели на классы

class A
{
private: 
	int y;
public: 
	int f() {return x;}

	A(int x, int t = 2){}
};

class B : public A
{
private:
	int z;
public:
	int f() {return A::f() + z;} 
	B() : A(3) {}	//так можно иниц родителей
protected:

};

A *a = new A;
B *b = new B;

a->f();
b->f();

A *p;
p = b; //указатель на A внутри B;
p->f(); //из A
class A
{
private: 
	int y;
public: 
	virtual int f() {return x;}	//вызывается в соотствие с типом обьекта
    //, а не типом указателя 

	A(int x, int t = 2){}
};

class B : public A
{
private:
	int z;
public:
	int f() {return A::f() + z;} 
	B() : A(3) {}	//так можно иниц родителей
protected:

};

A *a = new A;
B *b = new B;

a->f();
b->f();

A *p;
p = a;
p->f(); //из A
p = b; //указатель на A внутри B;
p->f(); //из B

delete p; //освободит память B, но вызовет только деструктор A

Конструкторы не бывают виртуальным (очевидно) Деструкторы бывают виртуальными (очевидно)

Таблица виртуальных функций лежит в A и это дополнительный вызов в память

виртуальные функции будут виртуальными во всех наследниках по умолчанию

Можно написать virtual f() = 0;, означает что здесь нет реализации f, этот класс будет абстрактным. Мы не сможем создать копию абстрактного класса, но можем создать на него указатель. Абстрактный класс не жизнеспособен самостоятельно, но жизнеспособен (и его копия есть) в составе наследника, который реализовал все абстрактные функции. (можно написать только для virtual и 0)

Множественное наследование

(есть только в C++ и Python)

class B: public A, public C 
{
	//...
}

Представим ситуацию

A -> B  -> D
  -> C  

B и C наследуются от A, D наследуется от B и C.

В D будет две разные реализации A!

Можно обьединить, надо поставить модификатор virtual в классах B и C при наследование от A - class B : virtual public A. (не бесплатно, тоже таблица виртуальности)

virtual ставишь там где раcходятся реализацции на класс который не хочешь чтобы в будущем размножился.