Динамическое управление памятью


В 11 стандарте С++ появились полезные умные указатели управления динамической памятью std::unique_ptr, std::shared_ptr и std::weak_ptr.

std::unique_ptr – умный указатель, который: получает единоличное владение объектом через его указатель, и разрушает объект через его указатель, когда unique_ptr выходит из области видимости.

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

Существует две версии std::unique_ptr:

1) управляет временем жизни одного объекта, например, созданного с помощью оператора new;

2) управляет временем жизни массива, с длиной, определенной во время выполнения, созданного с помощью new[].

Типичные случаи применения std::unique_ptr включают:

· обеспечение безопасности исключений для классов и функций, которые управляют объектами с динамическим временем жизни, гарантируя удаление в случае нормального завершения и завершения по исключению;

· передача владения динамически созданным объектом в функции;

· получение владения динамически созданным объектом из функций;

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

 

#include "stdafx.h"

#include <iostream>

#include <memory>

 

struct Foo {

Foo() { std::cout << "Foo::Foo\n"; }

~Foo() { std::cout << "Foo::~Foo\n"; }

void bar() { std::cout << "Foo::bar\n"; }

};

 

void f(const Foo &foo)

{

std::cout << "f(const Foo&)\n";

}

 

int main()

{

std::unique_ptr<Foo> p1(new Foo); // p1 владеет Foo

if (p1) p1->bar();

{

std::unique_ptr<Foo> p2(std::move(p1)); // теперь p2 владеет Foo

f(*p2);

p1 = std::move(p2); // владение возвращено p1

std::cout << "destroying p2...\n";

}

if (p1) p1->bar();

}

std::shared_ptr – умный указатель, с разделяемым владением объектом через его указатель. Несколько указателей shared_ptr могут владеть одним и тем же объектом; объект будет уничтожен, когда последний shared_ptr, указывающий на него, будет уничтожен или сброшен. Объект уничтожается с использованием delete-expression или с использованием пользовательской функции удаления объекта, переданной в конструктор shared_ptr.

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

В типичной реализации, std::shared_ptr содержит только два указателя:

· указатель на объект владения;

· указатель на блок управления;

где блок управления является динамически-созданным объект, который содержит:

· указатель на управляемый объект или сам управляемый объект;

· функцию удаления объекта;

· аллокатор;

· счетчик указателей shared_ptr, владеющих управляемым объектом;

· счетчик указателей weak_ptr, которые ссылаются на управляемый объект.

Деструктор shared_ptr уменьшает счетчик общих владельцев в контрольном блоке, и если этот счетчик достиг нуля, блок управления вызывает деструктор управляемого объекта, однако блок управления не освобождает себя до тех пор, пока счетчик std::weak_ptr также не достигнет нуля.

std::weak_ptr – умный указатель, который содержит "слабую" ссылку на объект, управляемый указателем std::shared_ptr. Чтобы получить доступ к управляемому объекту, указатель необходимо привести к типу std::shared_ptr, .

 

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

Помимо этого, std::weak_ptr используется для устранения циклических ссылок std::shared_ptr. Например, в таблице ниже приведён пример возникновения циклической сылки при использовании shared_ptr, а также решение этой проблемы с помощью weak_ptr.

 

Циклическая ссылка, которя приводит к утечке памяти. Устранение проблемы циклической ссылки.
#include “stdafx.h” #include <iostream> #include <memory>   using namespace std; struct Child; struct Parent { shared_ptr<Child> child; ~Parent() { cout << “Bye Parent” << endl; } void hi() const { cout << “Hello” << endl; } };   struct Child { shared_ptr<Parent> parent; ~Child() { cout << “Bye Child” << endl; } };   int main() { auto parent = make_shared<Parent>(); //3 auto child = make_shared<Child>(); //4 parent->child = child; //5 child->parent = parent; //6 child->parent->hi(); } //7 #include “stdafx.h” #include <iostream> #include <memory>   using namespace std; struct Child; struct Parent { shared_ptr<Child> child; //1 ~Parent() { cout << “Bye Parent” << endl; } void hi() const { cout << “Hello” << endl; } };   struct Child { weak_ptr<Parent> parent; //2 ~Child() { cout << “Bye Child” << endl; } };   int main() { auto parent = make_shared<Parent>(); //3 auto child = make_shared<Child>(); //4 parent->child = child; //5 child->parent = parent; //6 child->parent.lock()->hi(); } //7
Hello Hello Bye Parent Bye Child
В функции main() в строке //3 с помощью функции make_shared создаются в динамической памяти объекты: Parent и блок управлния этим объектом. Кроме того, функция make_shared возвращает объект типа shared_ptr, который содержат два указателя (на объект Parent и блок управлния этим объектом). В блоке управления есть два счётчика (sp и wp), в которых хранится количество существующих в программе указателей типа shared_ptr и в weak_ptr на объект Parent соответственно. После выполнения этой строки sp = 1, а wp = 0.
В функции main() в строке //4 с помощью функции make_shared создаются в динамической памяти объекты: Child и блок управлния этим объектом. Кроме того, функция make_shared возвращает объект типа shared_ptr, который содержат два указателя (на объект Child и блок управлния этим объектом). В блоке управления есть два счётчика (sc и wc), в которых хранится количество существующих в программе указателей типа shared_ptr и в weak_ptr на объект Child соответсвенно. После выполнения этой строки sc = 1, а wc = 0.
После выполнения строки //5 в программе теперь два указателя типа shared_ptr на объект Child, следовательно, в блоке управления этим объектом счётчики станут равными sс = 2, а wс = 0
После выполнения строки //6 в программе теперь два указателя типа shared_ptr на объект Parent, следовательно, в блоке управления этим объектом счётчики станут равными sp = 2, а wp= 0 После выполнения строки //6 в программе теперь коме shared_ptr есть указателя типа weak__ptr на объект Parent, следовательно, в блоке управления этим объектом счётчики станут равными sp = 1, а wp = 1
В строке //7 начинают уничтожаются локальные объекты функции main, т.е. указатели auto parent и auto child. В результате в блоках управления этих указателей счётчики становятся равными sp = 1, wp = 0 и sс = 1, а wс = 0, соотетственно. Поскольку счётчики sp и sc не равны нулю, то деструкторы для объектов Parent и Child не вызываются. Таким образом, они остаются в динамической памяти, что приводит к её утечке. В строке //7 начинают уничтожаются локальные объекты функции main, т.е. указатели auto parent и auto child. В результате в блоках управления этих указателей счётчики становятся равными sp = 0, wp = 1 и sс = 1, wс = 0, соотетственно. Поскольку счётчики sp = 0, то вызывается деструктор для объекта Parent и далее деструкторы для компонентных данных объекта Parent, т.е. деструктор для указателя shared_ptr<Child> child из строки //1, в результате sс = 0, а wс = 0. Как только sc = 0, сразу вызывается деструктор для объекта Child, который в свою очередь вызывате деструктор для weak_ptr<Parent> parent из строки //2 (хотя объект Parent уже разрушен, но блок управления им ещё нет, так как wp = 1), который обнуляет wp. После чего блок управления объектом Parent тоже разрушается. Таким образом, все динамические объекты разрушаются, и динамическая память полностью освобождается.

 

Модуль 6



Дата добавления: 2020-12-11; просмотров: 391;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.016 сек.