Лекция. Умные указатели. Наследование.

Умные указатели

	int  j = 10;//память выделится в стеке (stack)
	int* i = new int(10);//память выделится в куче (heap)

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

Умный указатель – структура, которая управляет указателем. В конструкторе ей передается указатель, а в деструкторе она его освобождает. В таком случае пользователю не приходится заботиться об утечке памяти.

Типы умных указателей

Scoped_ptr

  1. Использует стратегию «одного владельца» - единственный объект scoped_ptr владеет указателем, он же его и освобождает.
    	Picture* p = new Picture();//обычный указатель на объект типа Picture
    	Picture_ptr pp(new Picture());//умный указатель
    	
  2. Оператор присваивания и конструктор копирования объявляются с модификатором доступа private для запрета присваивания.

Shared_ptr

  1. Может быть много владельцев.
  2. Создается внешний счетчик ссылок, значение которого увеличивается каждый раз, когда добавляется еще один указатель на объект.
  3. Память освобождает последний, т.е. в деструкторе уменьшается значение счетчика, если после этого количество ссылок на объект равно 0, то память освобождается.
shared_ptr

Auto_ptr

  1. Как и Scoped_ptr реализует стратегию «одного владельца».
  2. При копировании владелец передается.
	Picture* a = new Picture(); 
	Auto_ptr b = a; //после этого а перестает указывать на объект
	

Intrusive_ptr

  1. Много владельцев.
  2. Объект сам управляет указателями.
  3. Объект сам себя удаляет.
В объекте заводится счетчик указателей, и методы addref() и release(), которые вызываются при создании и удалении указателя на объект. Следовательно, объект сам принимает решения.
	void Picture::release(){
		--m_refs;
		if(m_refs == 0){
			delete this;
		}
	}
Использование Release() корректно, если после вызова delete this; не происходит обращения к полям удаляемого объекта.
	Intrusive_Picture(Picture* p){
		if(p){
			p->addref();
		}
		//...
	}

	void Picture::addref(){
		++m_refs;
	}

Различия с Shared_ptr:

Замечание: интрузивные списки – указатели на следующий и предыдущий объекты хранятся в самом объекте.

Linked_ptr

  1. Много владельцев.
  2. Нет счетчика.
  3. Создается список указателей.
В каждом Linked_ptr кроме указателя на сам объект хранятся указатели на следующий и предыдущий Linked_ptr для того же объекта. linked_ptr
Scoped_ptr Shared_ptr Auto_ptr Intrusive_ptr Linked_ptr
  1. Стратегия одного владельца.
  2. Нет присваивания.
  1. Много владельцев.
  2. Внешний счетчик указателей.
  3. Освобождает память последний владелец.
  1. Один владелец.
  2. При копировании вледелец передается.
  1. Много владельцев.
  2. Объект, на который ссылается обычный указатель, сам управляет указателями на себя.
  3. Объект сам себя удаляет.
  1. Много владельцев.
  2. Нет счетчика.
  3. Создается список указателей.

Возможные проблемы в использовании умных указателей:


Если где-то в программе используются умные указатели для некоторого объекта, то стоит использовать их везде с этим типом.

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

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

struct OutputFile{
	//…
	void write(char const *);
};

struct MyOutputFile{
	//…
	void write(HTML const  &);
};

MyOutputFile можно унаследовать от OutputFile, тогда объектам MyOutputFile будут доступны поля и методы базовой структуры – OutputFile.
struct MyOutputFile : public OutputFile
{
	//…
};

При наследовании классов модификатор public указывать обязательно, для структур - нет. В случае, когда MyOutputFile унаследован от OutputFile метод write(HTML const &) полностью перекроет метод write(char const *) даже учитывая, что эти методы различаются типами передаваемых аргументов. Если в MyOutputFile добавить using OutputFile::write(), тогда метод write (HTML const &) не перекроет метод базового класса и у объектов MyOutputFile можно будет вызывать write(char const*).

При наследовании конструктор и деструктор не наследуются.

MyOutputFile(char const *filename)
				: OutputFile(filename){
	//…
}

Если в MyOutputFile определить метод с таким же именем и сигнатурой, как и в базовом, то обратиться к методу базового объекта можно через имя базового класса OutputFile::write(); Так же можно вызвать метод базового класса у объекта класса-наследника.

MyOutputFile mf(filename);
mf.OutputFile::write(“text”);

В памяти, выделенной под объект типа MyOutputFile, сначала расположены данные, унаследованные от базового класса, далее следуют данные относящиеся только к типу MyOutputFile.
Наследование может быть многоуровневым, то есть класс OutputFile может быть унаследован от какого-либо другого типа. При создании объекта класса-наследника сначала вызываются конструкторы всех базовых классов в порядке иерархии, начиная с самого верхнего. Деструкторы вызываются в обратном порядке.
Другой пример использования наследования:
inheritance_exmp

В классификации объектов отношение «является» приводит к неявному приведению.
Наследование:

	MyOutputFile f;
	OutputFile & of = f;
	of.write(“”);// вызовется метод  родителя

	Student s;
	Person p = s; 
В переменную p скопируются только поля, унаследованные от Person. Данный эффект называют срезкой.