From f58c8226b784cff4a6324f27286aff79340a17f5 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Wed, 27 Dec 2017 20:26:47 +0300 Subject: [PATCH 01/18] support asymptote --- Makefile | 2 ++ main.tex | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 3bc6a92..155e1b9 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ define clean_main_file rm -f ${MAIN_FILE_BASE}.aux $(MAIN_FILE_DEPS:%.tex=%.aux) rm -f ${MAIN_FILE_BASE}.log ${MAIN_FILE_BASE}.out rm -f ${MAIN_FILE_BASE}.pdf ${MAIN_FILE_BASE}.toc + rm -f ${MAIN_FILE_BASE}-*.{asy,pdf,pre,tex} rm -rf _minted-${MAIN_FILE_BASE} endef @@ -23,6 +24,7 @@ all: ${MAIN_FILE_BASE}.pdf ${HOW_TO_BASE}.pdf ${MAIN_FILE_BASE}.pdf: ${MAIN_FILE_BASE}.tex ${MAIN_FILE_DEPS} $(call clean_main_file) ${TEX_CMD} --interaction=nonstopmode --halt-on-error --shell-escape ${MAIN_FILE_BASE}.tex + find -name '${MAIN_FILE_BASE}-*.asy' -print0 | xargs -0 asy ${TEX_CMD} --interaction=nonstopmode --halt-on-error --shell-escape ${MAIN_FILE_BASE}.tex ${HOW_TO_BASE}.pdf: ${HOW_TO_BASE}.tex diff --git a/main.tex b/main.tex index c39c88e..3fe0a69 100644 --- a/main.tex +++ b/main.tex @@ -12,6 +12,8 @@ \usepackage{xcolor} \usepackage{hyperref} % for link +\usepackage[inline]{asymptote} + \definecolor{linkcolor}{HTML}{799B03} % цвет ссылок \definecolor{urlcolor}{HTML}{799B03} % цвет гиперссылок \hypersetup{colorlinks=true, linkcolor=linkcolor, urlcolor=urlcolor,} From 11205d22a094504162a689a2d204bd70147cc378 Mon Sep 17 00:00:00 2001 From: Ololoshechkin Date: Wed, 27 Dec 2017 15:20:51 +0300 Subject: [PATCH 02/18] initial commit --- asy_files/alloc.asy | 52 ++++ asy_files/copy.asy | 32 +++ asy_files/decart.asy | 32 +++ asy_files/move.asy | 32 +++ asy_files/pic1.asy | 54 +++++ asy_files/pic2.asy | 58 +++++ rvalue-references.tex | 140 ----------- smart-pointers.tex | 549 +++++++++++++++++++++++++++++++++++++++++- 8 files changed, 805 insertions(+), 144 deletions(-) create mode 100644 asy_files/alloc.asy create mode 100644 asy_files/copy.asy create mode 100644 asy_files/decart.asy create mode 100644 asy_files/move.asy create mode 100644 asy_files/pic1.asy create mode 100644 asy_files/pic2.asy diff --git a/asy_files/alloc.asy b/asy_files/alloc.asy new file mode 100644 index 0000000..8fa12a1 --- /dev/null +++ b/asy_files/alloc.asy @@ -0,0 +1,52 @@ +settings.outformat = "pdf"; + +real linked = 245; +real shared = 748; +real intrusive = 223; +real unique = 263; +real bench_width = 370.0; +real max_height = 748; + +real linked_offset = bench_width; +real shared_offset = 2 * bench_width; +real intrusive_offset = 3 * bench_width; +real unique_offset = 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +path lin = box((0,0), (linked_offset,linked)); +fill(lin, green); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, blue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, red); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +label("allocation/deallocation benchmark : ", (bench_width * 2.0, max_height * 1.1)); +real linked = 172; +real shared = 345; +real intrusive = 191; +real unique = 172; +path lin = box((0,0), (linked_offset,linked)); +fill(lin, black); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, black); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, black); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, black); +real label_heigth=bench_width / 4.0; +label("$heap allocation$", (linked_offset - label_offset, label_heigth), white); +label("$heap allocation$", (shared_offset - label_offset, label_heigth), white); +label("$heap allocation$", (intrusive_offset - label_offset, label_heigth), white); +label("$heap allocation$", (unique_offset - label_offset, label_heigth), white); diff --git a/asy_files/copy.asy b/asy_files/copy.asy new file mode 100644 index 0000000..0b42bb4 --- /dev/null +++ b/asy_files/copy.asy @@ -0,0 +1,32 @@ +settings.outformat = "pdf"; + +real linked = 49; +real shared = 75; +real intrusive = 55; +real unique = 0; +real bench_width = 44.0; +real max_height = 75; + +real linked_offset = bench_width; +real shared_offset = 2 * bench_width; +real intrusive_offset = 3 * bench_width; +real unique_offset = 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +path lin = box((0,0), (linked_offset,linked)); +fill(lin, green); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, blue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, red); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/decart.asy b/asy_files/decart.asy new file mode 100644 index 0000000..9d29e57 --- /dev/null +++ b/asy_files/decart.asy @@ -0,0 +1,32 @@ +settings.outformat = "pdf"; + +real linked = 2820; +real shared = 5716; +real intrusive = 2307; +real unique = 3423; +real bench_width = 3566.0; +real max_height = 5716; + +real linked_offset = bench_width; +real shared_offset = 2 * bench_width; +real intrusive_offset = 3 * bench_width; +real unique_offset = 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +path lin = box((0,0), (linked_offset,linked)); +fill(lin, green); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, blue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, red); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +label("real usecase (decart treee) benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/move.asy b/asy_files/move.asy new file mode 100644 index 0000000..73fff49 --- /dev/null +++ b/asy_files/move.asy @@ -0,0 +1,32 @@ +settings.outformat = "pdf"; + +real linked = 57; +real shared = 57; +real intrusive = 48; +real unique = 45; +real bench_width = 52.0; +real max_height = 57; + +real linked_offset = bench_width; +real shared_offset = 2 * bench_width; +real intrusive_offset = 3 * bench_width; +real unique_offset = 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +path lin = box((0,0), (linked_offset,linked)); +fill(lin, green); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, blue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, red); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/pic1.asy b/asy_files/pic1.asy new file mode 100644 index 0000000..38edf48 --- /dev/null +++ b/asy_files/pic1.asy @@ -0,0 +1,54 @@ +settings.outformat = "pdf"; + +real arrow_len = 5; +real box_h = arrow_len; +real box_w = 2.0 * box_h; +real box_count = 3; +real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; + +real obj_x0 = 0; +real obj_x1 = obj_x0 + object_box_w; +real obj_y0 = 0; +real obj_y1 = box_h; + +real obj_label_x = (obj_x0 + obj_x1) / 2.0; +real obj_label_y = (obj_y0 + obj_y1) / 2.0; + +real box_y0 = obj_y1 + arrow_len; +real box_y1 = box_y0 + box_h; + +real box_label_x_offset = box_w / 2.0; +real box_label_y_offset = box_h / 2.0; + +real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; +real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_down_offset = box_w * 0.5; +size(10cm,0); + +// green box: +path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); +fill(obj, green); +// it's label : +label("$O B J E C T$", (obj_label_x, obj_label_y), white); + +for (int i = 1; i <= box_count; ++i) { + real x0 = arrow_len * i + box_w * (i - 1); + real x1 = x0 + box_w; + + // blue box: + path ptr = box((x0, box_y0), (x1, box_y1)); + fill(ptr, blue); + // it's label: + label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); + + // left arrows: + draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); + draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); + + // right arrows: + draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); + draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); + + // down arrow: + draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +} \ No newline at end of file diff --git a/asy_files/pic2.asy b/asy_files/pic2.asy new file mode 100644 index 0000000..2aedeb3 --- /dev/null +++ b/asy_files/pic2.asy @@ -0,0 +1,58 @@ +settings.outformat = "pdf"; + +real arrow_len = 5; +real box_h = arrow_len; +real box_w = 2.0 * box_h; +real box_count = 4; +real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; + +real obj_x0 = 0; +real obj_x1 = obj_x0 + object_box_w; +real obj_y0 = 0; +real obj_y1 = box_h; + +real obj_label_x = (obj_x0 + obj_x1) / 2.0; +real obj_label_y = (obj_y0 + obj_y1) / 2.0; + +real box_y0 = obj_y1 + arrow_len; +real box_y1 = box_y0 + box_h; + +real box_label_x_offset = box_w / 2.0; +real box_label_y_offset = box_h / 2.0; + +real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; +real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_down_offset = box_w * 0.5; +size(10cm,0); + +// green box: +path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); +fill(obj, green); +// it's label : +label("$O B J E C T$", (obj_label_x, obj_label_y), white); + +for (int i = 1; i <= box_count; ++i) { + real x0 = arrow_len * i + box_w * (i - 1); + real x1 = x0 + box_w; + + // blue box: + path ptr = box((x0, box_y0), (x1, box_y1)); + fill(ptr, blue); + // it's label: + real nmb = i; + if (i >= 3) { + nmb = 7 - i; + } + label("$ptr" + string(nmb) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); + + // left arrows: + draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); + draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); + + // right arrows: + draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); + draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); + + // down arrow: + draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +} \ No newline at end of file diff --git a/rvalue-references.tex b/rvalue-references.tex index 92ad1a8..8a0484c 100644 --- a/rvalue-references.tex +++ b/rvalue-references.tex @@ -38,146 +38,6 @@ \subsection{Что можно положить в вектор?} \end{minted} \end{enumerate} -\subsection{Shared\_ptr} -\mintinline{c++}{std::shared_ptr} - это умный указатель, с разделяемым владением объектов через его указатель. Несколько указателей могут владеть одним объектом. Объект будет уничтожен, когда последний \mintinline{c++}{shared_ptr} будет уничтожен или сброшен. - -\textcolor{red}{NB}) \mintinline{c++}{shared_ptr} может не указывать ни на какой объект. - -\subsubsection{Наивная реализация.} - -\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -template -struct shared_ptr { - T* obj; // указатель на объект управления - size_t* counter; // счетчик ссылок, обнуление которого влечет удаление объекта - D *deleter; // функция, которая удаляет объект - shared_ptr(*T, D const& deleter); -} - -\end{minted} - - - -\textcolor{red}{NB}) \textbf{Зачем делитер, если можно всегда вызвать деструктор объекта?} Ответ: Ингда мы хотим реализовать следующую схему пользования объектом: 1) мы берем объект из ресурса. 2) пользуемся им. 3) Потом возвращаем его от куда взяли, когда он стал нам не нужен. - -\subsubsection{Оптимизация по памяти.} -На самом деле все немного сложнее: нам может понядобиться хранить в нашем smart\_ptr аллокатор, счетчик weak\_ptr (об этом позднее), указалеть на объект управления. Поэтому имеет смысл хранить все это в одном объекте, для оптимизации выделения памяти под smart\_ptr. С учетом выше указанного: - -\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -template -struct shared_ptr { - T* obj; // указатель на объект управления - struct control_block { - size_t* counter; // счетчик ссылок, обнуление которого влечет удаление объекта - D *deleter; // функция, которая удаляет объект - }* con_bl; - shared_ptr(*T, D const& deleter); -}; - -\end{minted} - - -\textcolor{red}{NB}) если создавать shared\_ptr с помощью конструктора, то память будет распределяться как выше указанно, но если создать shared\_ptr с помошью \mintinline{c++}{std::make_shared(new T)}\footnote{создает объект и оборачивает его в shared\_ptr}, то память выделится только один раз, и объект будет лежать рядом с блоком управления. Поэтому лучше использовать последний способ где это возможно, чтобы снизить оверхед от "умности"\ указателя. - -\subsubsection{Специальный конструтор.} -Иногда мы хотим продлевать объекту жизнь, если что-то ссылается на его поля. Для этого существует специальный конструтор: - -\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -struct car { - Wheel wheel[4]; -} -// У нас есть машина с колесами. Мы хотим, чтобы пока мы умеет живой указатель на колесо, машина тоже не удалялась вместе с колесами. -/* ... */ -shared_ptr p; -shared_ptr q(&p, wheel[2]); // мы переиспользуем p.counter для q. - -\end{minted} -Для этого наверно, будет полезно иметь ссылку на объект в блоке управления.\footnote{Мнение автора.} - - -\textcolor{red}{NB}) Еще одно применение этой фичи: мы хотим хранить дерево по shared\_ptr, чтобы когда мы удалили корень, дерево само удалиться. Тогда с помощью этого, конструктора можно гарантировать, что если есть указатель на вершины дерева, то можно гарантировать, что по нему можно пробежаться. - -\subsubsection{Заметки по использованию.} -\begin{enumerate} - \item - Возможно глупый пример, но все же. - \begin{minted}[ - linenos, - frame=lines, - framesep=2mm, - tabsize = 4, - breaklines] - {c++} - - int *silly_ptr = new int(5); - shared_ptr a1(silly_ptr); - shared_ptr b1(silly_ptr); - // b1.counter = a1.counter = 1; - // разные управляющие блоки - shared_ptr a2(silly_ptr); - shared_ptr b2(a2); - // b2.counter = a2.counter = 2; - // одинаковые - - \end{minted} - - -\end{enumerate} -\subsubsection{Weak\_ptr} -\mintinline{c++}{std::weak_ptr} - умный указатель, который моделирует временное владение объектом. - - -Ситуация: мы хотим хешировать картинки, которые хранять по shared\_ptr. То есть нам нужен мэп: name\_image $\to$ shared\_ptr. Но есть проблема, что если мы будем хранить в мэпе shared\_ptr, то они будут считать владельцами этих картинок, и они никогда не будут удаляться (если только их не удалить руками). В этой ситуации может помочь \mintinline{c++}{std::weak_ptr}. - - -Дело в том, что weak\_ptr содержит "слабую"\ ссылку на объект, управляемый указателем shared\_ptr. Для этого и нужен счетчик "слабых" сслылок в блоке управления shared\_ptr. Теперь блок управления считает еще и слабые сслыки и когда удаляется управляемый объект, блок управления остается жить, пока на него ссылается хотя бы один weak\_ptr, чтобы сообщать им о существовании объекта. Например с помощью метода \mintinline[breaklines]{c++}{std::weak_ptr::expired() // проверяет удален ли объект.} - - -То есть все, что должен хранить внутри себя weak\_ptr - это указатель блок управления. - - -Теперь вернемся к нашей задаче: мы сделаем мэп: name\_image $\to$ weak\_ptr. -\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -map> cache; -shared_ptr load_image(string const& name) { - auto i = cache.find(name); - if (i != cache.end()) { - shared_ptr temp = i->second.lock(); // создаем shared_ptr, который управляет объектом, которым управляет i. Если объект удален temp будет пустой. - if (temp) {// проверка на то, что temp не пуст - return temp; - } - } - // если картинки нет в кэше загружаем ее. - shared_ptr temp = real_load(name); - cache[name] = temp; // переобразование weak_ptr(shared_ptr); - return temp; -} - -\end{minted} - - -\textcolor{red}{NB}) Преобразования между shared\_ptr и weak\_ptr - это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. - - -\textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости shared\_ptr. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. - -\subsection{Сast\_pointer} -Что выполнять безоспасное приведение умных указателей можно искользовать слудующий синтаксис: -\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -struct A {/*...*/}; -struct B: A {/*...*/}; - -shared_ptr foo; -shared_ptr bar; -/*...*/ -bar = make_shared(); - -foo = dynamic_pointer_cast(bar); -// иначе пришлось бы писать так: -foo = shared_ptr(dynamic_cast(bar.get())); - -\end{minted} - \subsection{Когда необходимо копирование?} В С++ мы можем принимать аргументы функции по значению или по ссылке. И возвращать также. Меняется то, передаем ли мы сам объект или только его копию(значение). То есть если мы не хотим, чтобы объект менялся, то мы передаем его по значению, иначе по ссылке. diff --git a/smart-pointers.tex b/smart-pointers.tex index 66b9ce8..15a2c54 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -1,6 +1,6 @@ \section{Умные указатели} -Рассмотрим следующий код: +Рассмотрим следующий код: \begin{minted}[ linenos, @@ -24,7 +24,7 @@ \section{Умные указатели} {c++} container* create_container() { - container* c = new container(); + container* c = new container(); try { fill(*c); @@ -56,7 +56,7 @@ \section{Умные указатели} \end{minted} \subsection{unique\_ptr} -Самым простым умным указателем является \mintinline{c++}{std::unique_ptr}. +Самым простым умным указателем является \mintinline{c++}{std::unique_ptr}. Внутри себя \mintinline{c++}{std::unique_ptr} хранит один указатель \mintinline{c++}{T* ptr} и делает ему \mintinline{c++}{delete} в дескрукторе. \begin{minted}[ @@ -149,7 +149,7 @@ \subsection{unique\_ptr} {c++} unique_ptr& operator=(unique_ptr&& other) noexcept { - reset(other.release()); + reset(other.release()); return *this; } @@ -164,3 +164,544 @@ \subsection{Владение} Термин владение применяется не только к умным указателям, например можно сказать, что std::vector владеет памятью выделенной под свои элементы (обязан её освободить), а также владеет своими элементами (обязан вызывать им деструктор). В некоторых случаях объект может иметь несколько владельцев. Это называется разделяемым владением и работает следующим образом: пока существует хотя бы один владелец объект продолжает жить, когда пропадает последний владелец --- объект удаляется. Для умных указателей существует два способа реализации разделяемого владения: подсчет ссылок и провязка всех владельцев в двусвязный список. Оба подхода имеют свои преимущества и недостатки. Подсчет ссылок применяется во включенном в стандартную библиотеку указателе std::shared\_ptr. Указатель использующий провязку владельцев в двусвязный список в стандартной библиотеке отсутствует, но часто называется linked\_ptr. + +\subsection{Shared\_ptr} +\mintinline{c++}{std::shared_ptr} - это умный указатель, с разделяемым владением объектов через его указатель. Несколько указателей могут владеть одним объектом. Объект будет уничтожен, когда последний \mintinline{c++}{shared_ptr} будет уничтожен или сброшен. + +\textcolor{red}{NB}) \mintinline{c++}{shared_ptr} может не указывать ни на какой объект. + +\subsubsection{Наивная реализация.} + +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +struct shared_ptr { + T* obj; // указатель на объект управления + size_t* counter; // счетчик ссылок, обнуление которого влечет удаление объекта + D *deleter; // функция, которая удаляет объект + shared_ptr(*T, D const& deleter); +} + +\end{minted} + + + +\textcolor{red}{NB}) \textbf{Зачем делитер, если можно всегда вызвать деструктор объекта?} Ответ: Ингда мы хотим реализовать следующую схему пользования объектом: 1) мы берем объект из ресурса. 2) пользуемся им. 3) Потом возвращаем его от куда взяли, когда он стал нам не нужен. + +\subsubsection{Оптимизация по памяти.} +На самом деле все немного сложнее: нам может понядобиться хранить в нашем smart\_ptr аллокатор, счетчик weak\_ptr (об этом позднее), указалеть на объект управления. Поэтому имеет смысл хранить все это в одном объекте, для оптимизации выделения памяти под smart\_ptr. С учетом выше указанного: + +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +struct shared_ptr { + T* obj; // указатель на объект управления + struct control_block { + size_t* counter; // счетчик ссылок, обнуление которого влечет удаление объекта + D *deleter; // функция, которая удаляет объект + }* con_bl; + shared_ptr(*T, D const& deleter); +}; + +\end{minted} + + +\textcolor{red}{NB}) если создавать shared\_ptr с помощью конструктора, то память будет распределяться как выше указанно, но если создать shared\_ptr с помошью \mintinline{c++}{std::make_shared(new T)}\footnote{создает объект и оборачивает его в shared\_ptr}, то память выделится только один раз, и объект будет лежать рядом с блоком управления. Поэтому лучше использовать последний способ где это возможно, чтобы снизить оверхед от "умности"\ указателя. + +\subsubsection{Специальный конструтор.} +Иногда мы хотим продлевать объекту жизнь, если что-то ссылается на его поля. Для этого существует специальный конструтор: + +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +struct car { + Wheel wheel[4]; +} +// У нас есть машина с колесами. Мы хотим, чтобы пока мы умеет живой указатель на колесо, машина тоже не удалялась вместе с колесами. +/* ... */ +shared_ptr p; +shared_ptr q(&p, wheel[2]); // мы переиспользуем p.counter для q. + +\end{minted} +Для этого наверно, будет полезно иметь ссылку на объект в блоке управления.\footnote{Мнение автора.} + + +\textcolor{red}{NB}) Еще одно применение этой фичи: мы хотим хранить дерево по shared\_ptr, чтобы когда мы удалили корень, дерево само удалиться. Тогда с помощью этого, конструктора можно гарантировать, что если есть указатель на вершины дерева, то можно гарантировать, что по нему можно пробежаться. + +\subsubsection{Заметки по использованию.} +\begin{enumerate} + \item + Возможно глупый пример, но все же. + \begin{minted}[ + linenos, + frame=lines, + framesep=2mm, + tabsize = 4, + breaklines] + {c++} + + int *silly_ptr = new int(5); + shared_ptr a1(silly_ptr); + shared_ptr b1(silly_ptr); + // b1.counter = a1.counter = 1; + // разные управляющие блоки + shared_ptr a2(silly_ptr); + shared_ptr b2(a2); + // b2.counter = a2.counter = 2; + // одинаковые + + \end{minted} + + +\end{enumerate} +\subsubsection{Weak\_ptr} +\mintinline{c++}{std::weak_ptr} - умный указатель, который моделирует временное владение объектом. + + +Ситуация: мы хотим хешировать картинки, которые хранять по shared\_ptr. То есть нам нужен мэп: name\_image $\to$ shared\_ptr. Но есть проблема, что если мы будем хранить в мэпе shared\_ptr, то они будут считать владельцами этих картинок, и они никогда не будут удаляться (если только их не удалить руками). В этой ситуации может помочь \mintinline{c++}{std::weak_ptr}. + + +Дело в том, что weak\_ptr содержит "слабую"\ ссылку на объект, управляемый указателем shared\_ptr. Для этого и нужен счетчик "слабых" сслылок в блоке управления shared\_ptr. Теперь блок управления считает еще и слабые сслыки и когда удаляется управляемый объект, блок управления остается жить, пока на него ссылается хотя бы один weak\_ptr, чтобы сообщать им о существовании объекта. Например с помощью метода \mintinline[breaklines]{c++}{std::weak_ptr::expired() // проверяет удален ли объект.} + + +То есть все, что должен хранить внутри себя weak\_ptr - это указатель блок управления. + + +Теперь вернемся к нашей задаче: мы сделаем мэп: name\_image $\to$ weak\_ptr. +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +map> cache; +shared_ptr load_image(string const& name) { + auto i = cache.find(name); + if (i != cache.end()) { + shared_ptr temp = i->second.lock(); // создаем shared_ptr, который управляет объектом, которым управляет i. Если объект удален temp будет пустой. + if (temp) {// проверка на то, что temp не пуст + return temp; + } + } + // если картинки нет в кэше загружаем ее. + shared_ptr temp = real_load(name); + cache[name] = temp; // переобразование weak_ptr(shared_ptr); + return temp; +} + +\end{minted} + + +\textcolor{red}{NB}) Преобразования между shared\_ptr и weak\_ptr - это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. + + +\textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости shared\_ptr. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. + +\subsection{Сast\_pointer} +Что выполнять безоспасное приведение умных указателей можно искользовать слудующий синтаксис: +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +struct A {/*...*/}; +struct B: A {/*...*/}; + +shared_ptr foo; +shared_ptr bar; +/*...*/ +bar = make_shared(); + +foo = dynamic_pointer_cast(bar); +// иначе пришлось бы писать так: +foo = shared_ptr(dynamic_cast(bar.get())); + +\end{minted} + +\subsection{linked\_ptr} + +\mintinline{c++}{linked_ptr} --- умный указатель с разделяемым владением, реализованный с помощью двусвязного списка. + +\mintinline{c++}{linked_ptr} хранит в себе указатель на объект и два указателя на соседние \mintinline{c++}{linked_ptr}'ы в двусвязном списке. Для каждого двусвязного списка образованного из \mintinline{c++}{linked_ptr}'ов верно, что все указатели в нём владеют одним общим объектом. + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} +template struct linked_ptr +{ + ... +private: + T* ptr; + linked_ptr* prev; + linked_ptr* next; +}; +\end{minted} + +\begin{asy} +real arrow_len = 5; +real box_h = arrow_len; +real box_w = 2.0 * box_h; +real box_count = 3; +real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; + +real obj_x0 = 0; +real obj_x1 = obj_x0 + object_box_w; +real obj_y0 = 0; +real obj_y1 = box_h; + +real obj_label_x = (obj_x0 + obj_x1) / 2.0; +real obj_label_y = (obj_y0 + obj_y1) / 2.0; + +real box_y0 = obj_y1 + arrow_len; +real box_y1 = box_y0 + box_h; + +real box_label_x_offset = box_w / 2.0; +real box_label_y_offset = box_h / 2.0; + +real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; +real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_down_offset = box_w * 0.5; +size(10cm,0); + +// green box: +path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); +fill(obj, deepgreen); +// it's label : +label("$OBJECT$", (obj_label_x, obj_label_y), white); + +for (int i = 1; i <= box_count; ++i) { + real x0 = arrow_len * i + box_w * (i - 1); + real x1 = x0 + box_w; + + // blue box: + path ptr = box((x0, box_y0), (x1, box_y1)); + fill(ptr, royalblue); + // it's label: + label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); + + // left arrows: + draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); + draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); + + // right arrows: + draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); + draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); + + // down arrow: + draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +} +\end{asy} + +Присваивание \mintinline{c++}{linked_ptr} - это вставка в нужное место двусвязного списка. +\mintinline{c++}{auto ptr4 = ptr2;} + +\begin{asy} +real arrow_len = 5; +real box_h = arrow_len; +real box_w = 2.0 * box_h; +real box_count = 4; +real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; + +real obj_x0 = 0; +real obj_x1 = obj_x0 + object_box_w; +real obj_y0 = 0; +real obj_y1 = box_h; + +real obj_label_x = (obj_x0 + obj_x1) / 2.0; +real obj_label_y = (obj_y0 + obj_y1) / 2.0; + +real box_y0 = obj_y1 + arrow_len; +real box_y1 = box_y0 + box_h; + +real box_label_x_offset = box_w / 2.0; +real box_label_y_offset = box_h / 2.0; + +real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; +real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_down_offset = box_w * 0.5; +size(10cm,0); + +// green box: +path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); +fill(obj, green); +// it's label : +label("$O B J E C T$", (obj_label_x, obj_label_y), white); + +for (int i = 1; i <= box_count; ++i) { + real x0 = arrow_len * i + box_w * (i - 1); + real x1 = x0 + box_w; + + // blue box: + path ptr = box((x0, box_y0), (x1, box_y1)); + fill(ptr, blue); + // it's label: + real nmb = i; + if (i >= 3) { + nmb = 7 - i; + } + label("$ptr" + string(nmb) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); + + // left arrows: + draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); + draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); + + // right arrows: + draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); + draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); + + // down arrow: + draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +} +\end{asy} + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +linked_ptr(linked_ptr const& other) noexcept : + prev(other.prev), + next(other.next), + ptr(other.ptr) + {} + +\end{minted} + +Аналогично, при удалении \mintinline{c++}{linked_ptr} просто перенаправляются ссылки соседей и при необходимости удаояется сам объект: +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +~linked_ptr() noexcept { + if (!ptr) return; + if (prev == this) + delete ptr; + if (prev) prev->next = next; + if (next) next->prev = prev; +} + +\end{minted} + +Как видно, в \mintinline{c++}{linked_ptr} все элементы списка одновременно владеют ссылаемым объектом, но при этом имеют право удалять его только при удалении списка. + +\subsection{intrusive\_ptr} + +\mintinline{c++}{intrusive_ptr} - также разделяемый умный указатель. В stl его нет, но он есть в boost. Идея очень похожа на \mintinline{c++}{std::shared_ptr}, но в отличае от последнего в \mintinline{c++}{intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. Т.е. Реализовать его остается задачей пользователя. +Для корректной работы \mintinline{c++}{intrusive_ptr} надо реализовать функции: + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +int intrusive_ptr_add_ref(T* p); +int intrusive_ptr_release(T* p); + +\end{minted} + +Пример использования : + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +#include + +class Test { +public: + int ref_count; +}; + +void intrusive_ptr_add_ref(Test *p) { + ++p->ref_count; +} + +void intrusive_ptr_release(Test *p) { + if (0 == --p->ref_count) + delete p; +} + +int main() { + boost::intrusive_ptr p(new Test()); +} + +\end{minted} + +Замечание: \mintinline{c++}{intrusive_ptr} по понятным причинам эффективнее \mintinline{c++}{shared_ptr}, и если планируется использование разделяемого smart-pointer’а для него, то лучше юзать \mintinline{c++}{intrusive_ptr}, а в случае если приходится иметь дело с библиотечным классом, то \mintinline{c++}{std::shared_ptr}. + +\subsection{make\_shared / make\_unique} + +Как было сказано ранее, помимо стандартных конструкторов от указателя стандарт предлагает фабрики для создания объектов, определенные следующим образом (рассмотрим на примере \mintinline{c++}{std::shared_ptr}) : + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +template +shared_ptr make_shared(Args&&… args) { + return shared_ptr(new T(std::forward(args)...)); +} + +\end{minted} + +Исторически введение данного метода должно было решить проблему с гарантиями безопасности. Представим следующую ситуацию : +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +f(std::shared_ptr(new Class(“class”)), g()); + +\end{minted} + +При этом \mintinline{c++}{g()} бросает исключение. Т.к. по стандарту нет гарантии порядка вычисления аргументов \mintinline{c++}{f}, то может произойти следующее : сначала создадим \mintinline{c++}{new Class()}, выделим память, потом - вызовем \mintinline{c++}{g()}, которое бросит исключение, после чего конструктор \mintinline{c++}{shared_ptr} уже не вызовется, т.к. бросился exception. А т.к. мы нигде не пишем слово \mintinline{c++}{delete}, то произойдет memory leak. + +Теперь рассмотрим случай с \mintinline{c++}{make_shared} : + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +f(std::make_shared(“class”), g()); + +\end{minted} + +Если сначала вызовется \mintinline{c++}{g()}, то до конструктора \mintinline{c++}{Class} дело вообще не дойдет и упомянутой проблемы не возникнет. С другой стороны, если сначала вызовется \mintinline{c++}{make_shared}, то объект будет уже обернут в smart pointer и после исключения в \mintinline{c++}{g()} он будет автоматически удален!!! + +Другие приятные особенности фабрик вида \mintinline{c++}{make_pointer} : + +\begin{enumerate} +\item Они экономят аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new object()} и \mintinline{c++}{new counter}() происходит одна аллокация данных для пары <\mintinline{c++}{object}, \mintinline{c++}{counter}> +\item Мы теперь не используем в коде не только \mintinline{c++}{delete}, но и голый \mintinline{c++}{new}. (no naked new) +\end{enumerate} + +\subsection{Smart pointers pointing on this} + +Особый случай, когда нельзя просто так взять и использовать умные указатели бездумно - это указатели на this, которые возвращает метод класса. Рассмотрим код : + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +class SomeClass { + int data = 5; + std::shared_ptr f() { + return std::shared_ptr(this); + } + void foo() {} +} + +SomeClass obj; + +if (true) { // some scope + auto ptr = ob.f(); +} // exit scope (*) +obj.foo(); + +\end{minted} + +В этом примере при выхода из скоупа (*) т.к. умный указатель на obj создан всего 1, он вызовет деструктор \mintinline{c++}{obj}. Но при этом после этого мы уже не сможем его использовать (\mintinline{c++}{obj.foo()} уже некорректно). Поэтому для таких целей в stdlib есть интерфейс \mintinline{c++}{std::enable_shared_from_this}, оторый позволяет держать сильную ссылку на себя внутри самого объекта класса, который от него унаследован. В нашем случае : + + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +class SomeClass: std::enable_shared_from_this { + int data = 5; + std::shared_ptr f() { + return std::shared_ptr(this); + } + void foo() {} +} + + +SomeClass obj; + + +if (true) { + auto ptr = ob.f(); +} +obj.foo(); + +\end{minted} + +Такой код будет уже корректный. + +\subsection{smart pointers и наследование} + +В стандартной библиотеке умные указатели сделаны так, что если присваивания объекта типа \mintinline{c++}{A} может быть произведено в объект типа \mintinline{c++}{B}, то присваивания объекта типа \mintinline{c++}{smart_pointer} может быть произведено в объект типа \mintinline{c++}{smart_pointer}. +В том числе: + +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +shared_ptr dp1(new Derived); +shared_ptr bp1 = dp1; +shared_ptr bp2(dp1); +shared_ptr bp3(new Derived); + +\end{minted} +Это корректный код. Но стоит отметить, что в этом случае т.к. указатель внутри будет хранится на \mintinline{c++}{Derived} объект, то деструктор будет вызван ровно его. Поэтому если он не объявлен как виртуальный, то это UB. +Для явного каста указателей в стандартной библиотеке есть специальные методы: +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} + +derived_ptr = static_pointer_cast(base_ptr); + +\end{minted} +Этот стейтмент валиден т.и.т.т, когда \mintinline{c++}{static_cast(base_ptr.get())} - валидно, т.к. Ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Его основное преимущество - отсутствие необходимости писать \mintinline{c++}{.get()} вручную и тем самым необходимости работать с классическими голыми указателями. + +\subsection{доп. функции} +\begin{enumerate} +\item Умные указатели умеют каститься к \mintinline{c++}{bool} (\mintinline{c++}{true} т.и т.т., когда указываемый объект не \mintinline{c++}{nullptr}) +\item Также для них определены операции сравнения \mintinline{c++}{==}, \mintinline{c++}{!=}, \mintinline{c++}{<}, которые сравнивают голые указатели внутри. +\end{enumerate} + +\subsection{Performance test} + +Также уместно представить сравнение производительностей разных видов указателей: + +% \ASYinputx{g} +\begin{asy} +real linked = 51; +real shared = 75; +real intrusive = 55; +real unique = 0; +real bench_width = 45.0; +real max_height = 75; + +real linked_offset = bench_width; +real shared_offset = 2 * bench_width; +real intrusive_offset = 3 * bench_width; +real unique_offset = 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +path lin = box((0,0), (linked_offset,linked)); +fill(lin, green); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, blue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, red); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); +\end{asy} From 4dbc5e7c94e5a86250cf8ab9ded42d8e3618044d Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Fri, 29 Dec 2017 18:22:23 +0300 Subject: [PATCH 03/18] upd all critical comments --- asy_files/alloc.asy | 181 +++++++++-- asy_files/copy.asy | 109 +++++-- asy_files/decart.asy | 113 +++++-- asy_files/move.asy | 113 +++++-- asy_files/pic1.asy | 174 +++++++++-- asy_files/pic2.asy | 199 +++++++++--- smart-pointers.tex | 699 +++++++++++++++++++++++++++++++++++-------- 7 files changed, 1304 insertions(+), 284 deletions(-) diff --git a/asy_files/alloc.asy b/asy_files/alloc.asy index 8fa12a1..1be1d58 100644 --- a/asy_files/alloc.asy +++ b/asy_files/alloc.asy @@ -1,40 +1,118 @@ settings.outformat = "pdf"; -real linked = 245; -real shared = 748; -real intrusive = 223; -real unique = 263; -real bench_width = 370.0; -real max_height = 748; - -real linked_offset = bench_width; -real shared_offset = 2 * bench_width; -real intrusive_offset = 3 * bench_width; -real unique_offset = 4 * bench_width; +real linked = 236; +real shared = 604; +real intrusive = 180; +real unique = 225; +real linked_disp = 5; +real shared_disp = 8; +real intrusive_disp = 3; +real unique_disp = 1;real bench_width = 311.0; +real max_height = 604; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; real label_offset = (shared_offset - linked_offset) / 2.0; size(20cm,0); -path lin = box((0,0), (linked_offset,linked)); -fill(lin, green); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, blue); +fill(sha, royalblue); path intr = box((shared_offset,0), (intrusive_offset,intrusive)); fill(intr, magenta); path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, red); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("allocation/deallocation benchmark : ", (bench_width * 2.0, max_height * 1.1)); -real linked = 172; -real shared = 345; -real intrusive = 191; -real unique = 172; -path lin = box((0,0), (linked_offset,linked)); +real linked = 183; +real shared = 366; +real intrusive = 181; +real unique = 183; +real linked_disp = 1; +real shared_disp = 3; +real intrusive_disp = 0; +real unique_disp = 1;path lin = box((arrow_offset,0), (linked_offset,linked)); fill(lin, black); path sha = box((linked_offset,0), (shared_offset,shared)); @@ -45,8 +123,55 @@ fill(intr, black); path uniq = box((intrusive_offset,0), (unique_offset,unique)); fill(uniq, black); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + real label_heigth=bench_width / 4.0; -label("$heap allocation$", (linked_offset - label_offset, label_heigth), white); -label("$heap allocation$", (shared_offset - label_offset, label_heigth), white); -label("$heap allocation$", (intrusive_offset - label_offset, label_heigth), white); -label("$heap allocation$", (unique_offset - label_offset, label_heigth), white); +label("heap allocation", (linked_offset - label_offset, label_heigth), white); +label("heap allocation", (shared_offset - label_offset, label_heigth), white); +label("heap allocation", (intrusive_offset - label_offset, label_heigth), white); +label("heap allocation", (unique_offset - label_offset, label_heigth), white); diff --git a/asy_files/copy.asy b/asy_files/copy.asy index 0b42bb4..62d9441 100644 --- a/asy_files/copy.asy +++ b/asy_files/copy.asy @@ -1,32 +1,107 @@ settings.outformat = "pdf"; -real linked = 49; -real shared = 75; -real intrusive = 55; +real linked = 9; +real shared = 47; +real intrusive = 10; real unique = 0; -real bench_width = 44.0; -real max_height = 75; +real linked_disp = 0; +real shared_disp = 0; +real intrusive_disp = 0; +real unique_disp = 0;real bench_width = 16.0; +real max_height = 47; -real linked_offset = bench_width; -real shared_offset = 2 * bench_width; -real intrusive_offset = 3 * bench_width; -real unique_offset = 4 * bench_width; +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; real label_offset = (shared_offset - linked_offset) / 2.0; size(20cm,0); -path lin = box((0,0), (linked_offset,linked)); -fill(lin, green); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, blue); +fill(sha, royalblue); path intr = box((shared_offset,0), (intrusive_offset,intrusive)); fill(intr, magenta); path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, red); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/decart.asy b/asy_files/decart.asy index 9d29e57..7876237 100644 --- a/asy_files/decart.asy +++ b/asy_files/decart.asy @@ -1,32 +1,107 @@ settings.outformat = "pdf"; -real linked = 2820; -real shared = 5716; -real intrusive = 2307; -real unique = 3423; -real bench_width = 3566.0; -real max_height = 5716; - -real linked_offset = bench_width; -real shared_offset = 2 * bench_width; -real intrusive_offset = 3 * bench_width; -real unique_offset = 4 * bench_width; +real linked = 1187; +real shared = 3527; +real intrusive = 996; +real unique = 786; +real linked_disp = 5; +real shared_disp = 51; +real intrusive_disp = 18; +real unique_disp = 12;real bench_width = 1624.0; +real max_height = 3527; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; real label_offset = (shared_offset - linked_offset) / 2.0; size(20cm,0); -path lin = box((0,0), (linked_offset,linked)); -fill(lin, green); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + gray +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + gray +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + gray +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + gray +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, blue); +fill(sha, royalblue); path intr = box((shared_offset,0), (intrusive_offset,intrusive)); fill(intr, magenta); path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, red); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); label("real usecase (decart treee) benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/move.asy b/asy_files/move.asy index 73fff49..0017128 100644 --- a/asy_files/move.asy +++ b/asy_files/move.asy @@ -1,32 +1,107 @@ settings.outformat = "pdf"; -real linked = 57; -real shared = 57; -real intrusive = 48; -real unique = 45; -real bench_width = 52.0; -real max_height = 57; - -real linked_offset = bench_width; -real shared_offset = 2 * bench_width; -real intrusive_offset = 3 * bench_width; -real unique_offset = 4 * bench_width; +real linked = 7; +real shared = 2; +real intrusive = 2; +real unique = 2; +real linked_disp = 0; +real shared_disp = 0; +real intrusive_disp = 0; +real unique_disp = 0;real bench_width = 3.0; +real max_height = 7; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; real label_offset = (shared_offset - linked_offset) / 2.0; size(20cm,0); -path lin = box((0,0), (linked_offset,linked)); -fill(lin, green); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, blue); +fill(sha, royalblue); path intr = box((shared_offset,0), (intrusive_offset,intrusive)); fill(intr, magenta); path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, red); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/pic1.asy b/asy_files/pic1.asy index 38edf48..2ce0d56 100644 --- a/asy_files/pic1.asy +++ b/asy_files/pic1.asy @@ -1,16 +1,26 @@ settings.outformat = "pdf"; real arrow_len = 5; -real box_h = arrow_len; -real box_w = 2.0 * box_h; +real box_h = arrow_len / 3.0; +real box_w = box_h; real box_count = 3; -real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; +real object_box_w = 2 * box_w; -real obj_x0 = 0; +real get_x0(int i) { + return arrow_len * i + box_w * (i - 1); +} + +real get_x1(int i) { + return get_x0(i) + box_w; +} + +real obj_x0 = get_x0(2) - box_w / 2.0; real obj_x1 = obj_x0 + object_box_w; real obj_y0 = 0; real obj_y1 = box_h; +real eps = arrow_len / 10.0; + real obj_label_x = (obj_x0 + obj_x1) / 2.0; real obj_label_y = (obj_y0 + obj_y1) / 2.0; @@ -20,35 +30,133 @@ real box_y1 = box_y0 + box_h; real box_label_x_offset = box_w / 2.0; real box_label_y_offset = box_h / 2.0; -real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; -real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_left_offset = box_h * 2.0 / 3.0; +real arrow_right_offset = box_h * 1.0 / 3.0; +real arrow_left_y = box_y0 + arrow_left_offset; +real arrow_right_y = box_y0 + arrow_right_offset; real arrow_down_offset = box_w * 0.5; -size(10cm,0); -// green box: +real label_offset = box_h / 6.0; +size(15cm,0); + +void draw_arrow(path p, real relative_pos, string text, align al) { + draw( + L=Label( + scale(0.5)*("" + text + ""), + position=Relative(relative_pos), + gray, + align = al + ), + p, + arrow=Arrow + ); +} + +void draw_strait_arrows(real x0, real x1, real len = arrow_len) { + real rel_pos = 0.75 + 0.22 * (1 - arrow_len / len); + draw_arrow((x0 - len, arrow_left_y) -- (x0, arrow_left_y), rel_pos, "next", (E + N / 2.0)); + draw_arrow((x0, arrow_right_y) -- (x0 - len, arrow_right_y), rel_pos, "prev", (W - N / 2.0)); +} + +void draw_curved_arrow(pair p0, pair p3, string text, bool reversed = false) { + real delta = arrow_len * (1.0 / 3.0); + pair p1 = (xpart(p0) + delta, ypart(p0)); + pair p2 = (xpart(p3) - delta, ypart(p3)); + pair add = (arrow_len / 10.0, 0); + if (reversed) { + p1 -= add; + p2 -= add - (0, box_h / 10.0); + } else { + p1 += add - (0, box_h / 10.0); + p2 += add; + } + p2 += 0.3 * (p1 - p2); + p1 -= 0.3 * (p1 - p2); + pair p1_5 = (p1 + p2) / 2.0; + + //draw(circle(p1, 0.1), red); + //draw(circle(p1_5, 0.1), magenta); + //draw(circle(p2, 0.1), blue); + + path p = p0{right}..{down}p1{down}..{left}p1_5{left}..{down}p2{down}..{right}p3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); + + pair q0 = p3 + box_w, q1 = p2 + (box_w + 2 * (xpart(p3) - xpart(p2)), 0), q3 = p0 + arrow_len, q2 = p1 + (arrow_len - 2 * (xpart(p1) - xpart(p0)), 0); + pair q1_5 = (q1 + q2) / 2.0; + + //draw(circle(q1, 0.1), red); + //draw(circle(q1_5, 0.1), magenta); + //draw(circle(q2, 0.1), blue); + + p = q0{right}..{up}q1{up}..{left}q1_5{left}..{up}q2{up}..{right}q3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); +} + +void draw_arrow_to_obj(real x0, real y0) { + pair p0 = (x0, y0); + real obj_mid = (obj_x0 + obj_x1) / 2.0; + pair p1 = (obj_mid + (x0 - obj_mid) / 1.5, obj_y1 + arrow_len / 2.0 - abs(x0 - obj_mid) / 7.0); + pair p2 = (obj_mid+ (x0 - obj_mid) / 50.0, obj_y1); + if (x0 == obj_mid) { + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.7), + gray, + align=-N + W * 0.7 + ), + p0 -- p2, + arrow=Arrow + ); + } else if (x0 < obj_mid) + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.6), + gray, + align=-N + ), + p0{down}..{right}p1{right}..{down}p2, + arrow=Arrow + ); + else + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{left}p1{left}..{down}p2, + arrow=Arrow + ); +} + +void draw_box(real x0, real x1, int i, real y0 = box_y0, real y1 = box_y1, pen color=royalblue) { + draw_arrow_to_obj(x0 + arrow_down_offset, y0); + path ptr = box((x0, y0), (x1, y1)); + fill(ptr, color); + draw(ptr, black); + label("ptr" + string(i) + "", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); +} + +void draw_last_arrows() { + int last_index = (int)box_count + 1; + real last_x0 = get_x0(last_index) - arrow_len / 2; + real last_x1 = get_x1(last_index) - arrow_len / 2; + draw_strait_arrows(last_x0, last_x1, arrow_len / 2); +} + + path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); -fill(obj, green); -// it's label : -label("$O B J E C T$", (obj_label_x, obj_label_y), white); - -for (int i = 1; i <= box_count; ++i) { - real x0 = arrow_len * i + box_w * (i - 1); - real x1 = x0 + box_w; - - // blue box: - path ptr = box((x0, box_y0), (x1, box_y1)); - fill(ptr, blue); - // it's label: - label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); - - // left arrows: - draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); - draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); - - // right arrows: - draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); - draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); - - // down arrow: - draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); -} \ No newline at end of file +fill(obj, deepgreen); +draw(obj, black); +label("object", (obj_label_x, obj_label_y), white); + + +for (int i = 1; i <= box_count; ++i) { + real x0 = get_x0(i), x1 = get_x1(i); + draw_strait_arrows(x0, x1, i == 1 ? arrow_len / 2 : arrow_len); + draw_box(x0, x1, i); +} +draw_last_arrows(); \ No newline at end of file diff --git a/asy_files/pic2.asy b/asy_files/pic2.asy index 2aedeb3..acb3520 100644 --- a/asy_files/pic2.asy +++ b/asy_files/pic2.asy @@ -1,16 +1,29 @@ +//import unicode; settings.outformat = "pdf"; +//texpreamble("\setmainlanguage{english}\setotherlanguage{urdu}\newfontfamily\urdufont{Times New Roman}"); + real arrow_len = 5; -real box_h = arrow_len; -real box_w = 2.0 * box_h; -real box_count = 4; -real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; +real box_h = arrow_len / 3.0; +real box_w = box_h; +real box_count = 3; +real object_box_w = 2 * box_w; + +real get_x0(int i) { + return arrow_len * i + box_w * (i - 1); +} + +real get_x1(int i) { + return get_x0(i) + box_w; +} -real obj_x0 = 0; +real obj_x0 = get_x0(2) - box_w / 2.0; real obj_x1 = obj_x0 + object_box_w; real obj_y0 = 0; real obj_y1 = box_h; +real eps = arrow_len / 10.0; + real obj_label_x = (obj_x0 + obj_x1) / 2.0; real obj_label_y = (obj_y0 + obj_y1) / 2.0; @@ -20,39 +33,149 @@ real box_y1 = box_y0 + box_h; real box_label_x_offset = box_w / 2.0; real box_label_y_offset = box_h / 2.0; -real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; -real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_left_offset = box_h * 2.0 / 3.0; +real arrow_right_offset = box_h * 1.0 / 3.0; +real arrow_left_y = box_y0 + arrow_left_offset; +real arrow_right_y = box_y0 + arrow_right_offset; real arrow_down_offset = box_w * 0.5; -size(10cm,0); -// green box: +real label_offset = box_h / 6.0; +size(15cm,0); + +void draw_arrow(path p, real relative_pos, string text, align al) { + draw( + L=Label( + scale(0.5)*("" + text + ""), + position=Relative(relative_pos), + gray, + align = al + ), + p, + arrow=Arrow + ); +} + +void draw_strait_arrows(real x0, real x1, real len = arrow_len) { + real rel_pos = 0.75 + 0.22 * (1 - arrow_len / len); + draw_arrow((x0 - len, arrow_left_y) -- (x0, arrow_left_y), rel_pos, "next", (E + N / 2.0)); + draw_arrow((x0, arrow_right_y) -- (x0 - len, arrow_right_y), rel_pos, "prev", (W - N / 2.0)); +} + +void draw_curved_arrow(pair p0, pair p3, string text, bool reversed = false) { + real delta = arrow_len * (1.0 / 3.0); + pair p1 = (xpart(p0) + delta, ypart(p0)); + pair p2 = (xpart(p3) - delta, ypart(p3)); + pair add = (arrow_len / 10.0, 0); + if (reversed) { + p1 -= add; + p2 -= add - (0, box_h / 10.0); + } else { + p1 += add - (0, box_h / 10.0); + p2 += add; + } + p2 += 0.3 * (p1 - p2); + p1 -= 0.3 * (p1 - p2); + pair p1_5 = (p1 + p2) / 2.0; + + //draw(circle(p1, 0.1), red); + //draw(circle(p1_5, 0.1), magenta); + //draw(circle(p2, 0.1), blue); + + path p = p0{right}..{down}p1{down}..{left}p1_5{left}..{down}p2{down}..{right}p3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); + + pair q0 = p3 + box_w, q1 = p2 + (box_w + 2 * (xpart(p3) - xpart(p2)), 0), q3 = p0 + arrow_len, q2 = p1 + (arrow_len - 2 * (xpart(p1) - xpart(p0)), 0); + pair q1_5 = (q1 + q2) / 2.0; + + //draw(circle(q1, 0.1), red); + //draw(circle(q1_5, 0.1), magenta); + //draw(circle(q2, 0.1), blue); + + p = q0{right}..{up}q1{up}..{left}q1_5{left}..{up}q2{up}..{right}q3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); +} + +void draw_arrow_to_obj(real x0, real y0) { + pair p0 = (x0, y0); + real obj_mid = (obj_x0 + obj_x1) / 2.0; + pair p1 = (obj_mid + (x0 - obj_mid) / 1.5, obj_y1 + arrow_len / 2.0 - abs(x0 - obj_mid) / 7.0); + pair p2 = (obj_mid+ (x0 - obj_mid) / 50.0, obj_y1); + if (x0 == obj_mid) { + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.6), + gray, + align=-N + W * 0.7 + ), + p0 -- p2, + arrow=Arrow + ); + } else if (x0 < obj_mid) + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{right}p1{right}..{down}p2, + arrow=Arrow + ); + else + draw( + L=Label( + scale(0.5)*"ptr", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{left}p1{left}..{down}p2, + arrow=Arrow + ); +} + +void draw_box(real x0, real x1, int i, real y0 = box_y0, real y1 = box_y1, pen color=royalblue) { + draw_arrow_to_obj(x0 + arrow_down_offset, y0); + path ptr = box((x0, y0), (x1, y1)); + fill(ptr, color); + draw(ptr, black); + label("ptr" + string(i) + "", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); +} + +void draw_last_arrows() { + int last_index = (int)box_count + 1; + real last_x0 = get_x0(last_index) - arrow_len / 2; + real last_x1 = get_x1(last_index) - arrow_len / 2; + draw_strait_arrows(last_x0, last_x1, arrow_len / 2); +} + + path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); -fill(obj, green); -// it's label : -label("$O B J E C T$", (obj_label_x, obj_label_y), white); - -for (int i = 1; i <= box_count; ++i) { - real x0 = arrow_len * i + box_w * (i - 1); - real x1 = x0 + box_w; - - // blue box: - path ptr = box((x0, box_y0), (x1, box_y1)); - fill(ptr, blue); - // it's label: - real nmb = i; - if (i >= 3) { - nmb = 7 - i; - } - label("$ptr" + string(nmb) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); - - // left arrows: - draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); - draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); - - // right arrows: - draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); - draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); - - // down arrow: - draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); -} \ No newline at end of file +fill(obj, deepgreen); +draw(obj, black); +label("object", (obj_label_x, obj_label_y), white); + + +for (int i = 1; i <= 2; ++i) { + real x0 = get_x0(i), x1 = get_x1(i); + draw_strait_arrows(x0, x1, i == 1 ? arrow_len / 2 : arrow_len); + draw_box(x0, x1, i); +} +draw_last_arrows(); + +real x2_0 = get_x0(2), x2_1 = get_x1(2), x3_0 = get_x0(3), x3_1 = get_x1(3); +real x4_0 = x2_1 + (arrow_len - box_w) / 2.0; +real x4_1 = x4_0 + box_w; +real y4_0 = box_y0 - arrow_len / 2.0; +real y4_1 = y4_0 + box_h; + +draw_box(x3_0, x3_1, 3); + +draw_box(x4_0, x4_1, 4, y4_0, y4_1, brown); + +draw_curved_arrow((x2_1, arrow_left_y), (x4_0, y4_0 + arrow_left_offset), "next", false); +draw_curved_arrow((x2_1, arrow_right_y), (x4_0, y4_0 + arrow_right_offset), "prev", true); + +//draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); +//draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); diff --git a/smart-pointers.tex b/smart-pointers.tex index 15a2c54..73b582d 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -55,7 +55,7 @@ \section{Умные указатели} } \end{minted} -\subsection{unique\_ptr} +\subsection{\mintinline{c++}{std::unique_ptr}} Самым простым умным указателем является \mintinline{c++}{std::unique_ptr}. Внутри себя \mintinline{c++}{std::unique_ptr} хранит один указатель \mintinline{c++}{T* ptr} и делает ему \mintinline{c++}{delete} в дескрукторе. @@ -159,13 +159,13 @@ \subsection{unique\_ptr} \end{minted} \subsection{Владение} -Ответственность за удаление объекта называется владением. Например, unique\_ptr ответственен за удаление объекта на который он ссылается, соответственно говорят, что unique\_ptr владеет объектом, на который он ссылается. Про функцию \mintinline{c++}{reset(p)} говорят, что она передает владение объектом unique\_ptr'у, а функция \mintinline{c++}{release()}, наоборот, забирает владение объектом у unique\_ptr'а. +Ответственность за удаление объекта называется владением. Например, \mintinline{c++}{std::unique_ptr} ответственен за удаление объекта на который он ссылается, соответственно говорят, что \mintinline{c++}{std::unique_ptr} владеет объектом, на который он ссылается. Про функцию \mintinline{c++}{reset(p)} говорят, что она передает владение объектом \mintinline{c++}{std::unique_ptr}'у, а функция \mintinline{c++}{release()}, наоборот, забирает владение объектом у \mintinline{c++}{std::unique_ptr}'а. Термин владение применяется не только к умным указателям, например можно сказать, что std::vector владеет памятью выделенной под свои элементы (обязан её освободить), а также владеет своими элементами (обязан вызывать им деструктор). -В некоторых случаях объект может иметь несколько владельцев. Это называется разделяемым владением и работает следующим образом: пока существует хотя бы один владелец объект продолжает жить, когда пропадает последний владелец --- объект удаляется. Для умных указателей существует два способа реализации разделяемого владения: подсчет ссылок и провязка всех владельцев в двусвязный список. Оба подхода имеют свои преимущества и недостатки. Подсчет ссылок применяется во включенном в стандартную библиотеку указателе std::shared\_ptr. Указатель использующий провязку владельцев в двусвязный список в стандартной библиотеке отсутствует, но часто называется linked\_ptr. +В некоторых случаях объект может иметь несколько владельцев. Это называется разделяемым владением и работает следующим образом: пока существует хотя бы один владелец объект продолжает жить, когда пропадает последний владелец --- объект удаляется. Для умных указателей существует два способа реализации разделяемого владения: подсчет ссылок и провязка всех владельцев в двусвязный список. Оба подхода имеют свои преимущества и недостатки. Подсчет ссылок применяется во включенном в стандартную библиотеку указателе \mintinline{c++}{std::shared_ptr}. Указатель использующий провязку владельцев в двусвязный список в стандартной библиотеке отсутствует, но часто называется \mintinline{c++}{linked_ptr}. -\subsection{Shared\_ptr} +\subsection{\mintinline{c++}{std::shared_ptr}} \mintinline{c++}{std::shared_ptr} - это умный указатель, с разделяемым владением объектов через его указатель. Несколько указателей могут владеть одним объектом. Объект будет уничтожен, когда последний \mintinline{c++}{shared_ptr} будет уничтожен или сброшен. \textcolor{red}{NB}) \mintinline{c++}{shared_ptr} может не указывать ни на какой объект. @@ -204,7 +204,7 @@ \subsubsection{Оптимизация по памяти.} \end{minted} -\textcolor{red}{NB}) если создавать shared\_ptr с помощью конструктора, то память будет распределяться как выше указанно, но если создать shared\_ptr с помошью \mintinline{c++}{std::make_shared(new T)}\footnote{создает объект и оборачивает его в shared\_ptr}, то память выделится только один раз, и объект будет лежать рядом с блоком управления. Поэтому лучше использовать последний способ где это возможно, чтобы снизить оверхед от "умности"\ указателя. +\textcolor{red}{NB}) если создавать \mintinline{c++}{std::shared_ptr} с помощью конструктора, то память будет распределяться как выше указанно, но если создать \mintinline{c++}{std::shared_ptr} с помошью \mintinline{c++}{std::make_shared(new T)}\footnote{создает объект и оборачивает его в \mintinline{c++}{std::shared_ptr}}, то память выделится только один раз, и объект будет лежать рядом с блоком управления. Поэтому лучше использовать последний способ где это возможно, чтобы снизить оверхед от "умности"\ указателя. \subsubsection{Специальный конструтор.} Иногда мы хотим продлевать объекту жизнь, если что-то ссылается на его поля. Для этого существует специальный конструтор: @@ -222,7 +222,7 @@ \subsubsection{Специальный конструтор.} Для этого наверно, будет полезно иметь ссылку на объект в блоке управления.\footnote{Мнение автора.} -\textcolor{red}{NB}) Еще одно применение этой фичи: мы хотим хранить дерево по shared\_ptr, чтобы когда мы удалили корень, дерево само удалиться. Тогда с помощью этого, конструктора можно гарантировать, что если есть указатель на вершины дерева, то можно гарантировать, что по нему можно пробежаться. +\textcolor{red}{NB}) Еще одно применение этой фичи: мы хотим хранить дерево по \mintinline{c++}{std::shared_ptr}, чтобы когда мы удалили корень, дерево само удалиться. Тогда с помощью этого, конструктора можно гарантировать, что если есть указатель на вершины дерева, то можно гарантировать, что по нему можно пробежаться. \subsubsection{Заметки по использованию.} \begin{enumerate} @@ -254,10 +254,10 @@ \subsubsection{Weak\_ptr} \mintinline{c++}{std::weak_ptr} - умный указатель, который моделирует временное владение объектом. -Ситуация: мы хотим хешировать картинки, которые хранять по shared\_ptr. То есть нам нужен мэп: name\_image $\to$ shared\_ptr. Но есть проблема, что если мы будем хранить в мэпе shared\_ptr, то они будут считать владельцами этих картинок, и они никогда не будут удаляться (если только их не удалить руками). В этой ситуации может помочь \mintinline{c++}{std::weak_ptr}. +Ситуация: мы хотим хешировать картинки, которые хранять по \mintinline{c++}{std::shared_ptr}. То есть нам нужен мэп: name\_image $\to$ \mintinline{c++}{std::shared_ptr}. Но есть проблема, что если мы будем хранить в мэпе \mintinline{c++}{std::shared_ptr}, то они будут считать владельцами этих картинок, и они никогда не будут удаляться (если только их не удалить руками). В этой ситуации может помочь \mintinline{c++}{std::weak_ptr}. -Дело в том, что weak\_ptr содержит "слабую"\ ссылку на объект, управляемый указателем shared\_ptr. Для этого и нужен счетчик "слабых" сслылок в блоке управления shared\_ptr. Теперь блок управления считает еще и слабые сслыки и когда удаляется управляемый объект, блок управления остается жить, пока на него ссылается хотя бы один weak\_ptr, чтобы сообщать им о существовании объекта. Например с помощью метода \mintinline[breaklines]{c++}{std::weak_ptr::expired() // проверяет удален ли объект.} +Дело в том, что weak\_ptr содержит "слабую"\ ссылку на объект, управляемый указателем \mintinline{c++}{std::shared_ptr}. Для этого и нужен счетчик "слабых" сслылок в блоке управления \mintinline{c++}{std::shared_ptr}. Теперь блок управления считает еще и слабые сслыки и когда удаляется управляемый объект, блок управления остается жить, пока на него ссылается хотя бы один weak\_ptr, чтобы сообщать им о существовании объекта. Например с помощью метода \mintinline[breaklines]{c++}{std::weak_ptr::expired() // проверяет удален ли объект.} То есть все, что должен хранить внутри себя weak\_ptr - это указатель блок управления. @@ -283,10 +283,10 @@ \subsubsection{Weak\_ptr} \end{minted} -\textcolor{red}{NB}) Преобразования между shared\_ptr и weak\_ptr - это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. +\textcolor{red}{NB}) Преобразования между \mintinline{c++}{std::shared_ptr} и weak\_ptr - это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. -\textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости shared\_ptr. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. +\textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости \mintinline{c++}{std::shared_ptr}. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. \subsection{Сast\_pointer} Что выполнять безоспасное приведение умных указателей можно искользовать слудующий синтаксис: @@ -305,7 +305,7 @@ \subsection{Сast\_pointer} \end{minted} -\subsection{linked\_ptr} +\subsection{\mintinline{c++}{linked_ptr}} \mintinline{c++}{linked_ptr} --- умный указатель с разделяемым владением, реализованный с помощью двусвязного списка. @@ -321,23 +321,42 @@ \subsection{linked\_ptr} ... private: T* ptr; - linked_ptr* prev; - linked_ptr* next; + mutable linked_ptr* prev; + mutable linked_ptr* next; }; \end{minted} +Члены prev и next помечены как mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2. +Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : + +\begin{minipage}[h]{0.49\linewidth} +\centering \begin{asy} +import unicode; +settings.outformat = "pdf"; +texpreamble("\setmainlanguage{english}\setotherlanguage{urdu}\newfontfamily\urdufont{Times New Roman}"); + real arrow_len = 5; -real box_h = arrow_len; -real box_w = 2.0 * box_h; +real box_h = arrow_len / 3.0; +real box_w = box_h; real box_count = 3; -real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; +real object_box_w = 2 * box_w; + +real get_x0(int i) { + return arrow_len * i + box_w * (i - 1); +} -real obj_x0 = 0; +real get_x1(int i) { + return get_x0(i) + box_w; +} + +real obj_x0 = get_x0(2) - box_w / 2.0; real obj_x1 = obj_x0 + object_box_w; real obj_y0 = 0; real obj_y1 = box_h; +real eps = arrow_len / 10.0; + real obj_label_x = (obj_x0 + obj_x1) / 2.0; real obj_label_y = (obj_y0 + obj_y1) / 2.0; @@ -347,55 +366,170 @@ \subsection{linked\_ptr} real box_label_x_offset = box_w / 2.0; real box_label_y_offset = box_h / 2.0; -real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; -real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_left_offset = box_h * 2.0 / 3.0; +real arrow_right_offset = box_h * 1.0 / 3.0; +real arrow_left_y = box_y0 + arrow_left_offset; +real arrow_right_y = box_y0 + arrow_right_offset; real arrow_down_offset = box_w * 0.5; -size(10cm,0); -// green box: -path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); -fill(obj, deepgreen); -// it's label : -label("$OBJECT$", (obj_label_x, obj_label_y), white); +real label_offset = box_h / 6.0; +size(15cm,0); + +void draw_arrow(path p, real relative_pos, string text, align al) { + draw( + L=Label( + scale(0.5)*("$" + text + "$"), + position=Relative(relative_pos), + gray, + align = al + ), + p, + arrow=Arrow + ); +} + +void draw_strait_arrows(real x0, real x1, real len = arrow_len) { + real rel_pos = 0.75 + 0.22 * (1 - arrow_len / len); + draw_arrow((x0 - len, arrow_left_y) -- (x0, arrow_left_y), rel_pos, "next", (E + N / 2.0)); + draw_arrow((x0, arrow_right_y) -- (x0 - len, arrow_right_y), rel_pos, "prev", (W - N / 2.0)); +} + +void draw_curved_arrow(pair p0, pair p3, string text, bool reversed = false) { + real delta = arrow_len * (1.0 / 3.0); + pair p1 = (xpart(p0) + delta, ypart(p0)); + pair p2 = (xpart(p3) - delta, ypart(p3)); + pair add = (arrow_len / 10.0, 0); + if (reversed) { + p1 -= add; + p2 -= add - (0, box_h / 10.0); + } else { + p1 += add - (0, box_h / 10.0); + p2 += add; + } + p2 += 0.3 * (p1 - p2); + p1 -= 0.3 * (p1 - p2); + pair p1_5 = (p1 + p2) / 2.0; -for (int i = 1; i <= box_count; ++i) { - real x0 = arrow_len * i + box_w * (i - 1); - real x1 = x0 + box_w; + //draw(circle(p1, 0.1), red); + //draw(circle(p1_5, 0.1), magenta); + //draw(circle(p2, 0.1), blue); - // blue box: - path ptr = box((x0, box_y0), (x1, box_y1)); - fill(ptr, royalblue); - // it's label: - label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); + path p = p0{right}..{down}p1{down}..{left}p1_5{left}..{down}p2{down}..{right}p3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); - // left arrows: - draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); - draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); + pair q0 = p3 + box_w, q1 = p2 + (box_w + 2 * (xpart(p3) - xpart(p2)), 0), q3 = p0 + arrow_len, q2 = p1 + (arrow_len - 2 * (xpart(p1) - xpart(p0)), 0); + pair q1_5 = (q1 + q2) / 2.0; + + //draw(circle(q1, 0.1), red); + //draw(circle(q1_5, 0.1), magenta); + //draw(circle(q2, 0.1), blue); + + p = q0{right}..{up}q1{up}..{left}q1_5{left}..{up}q2{up}..{right}q3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); +} + +void draw_arrow_to_obj(real x0, real y0) { + pair p0 = (x0, y0); + real obj_mid = (obj_x0 + obj_x1) / 2.0; + pair p1 = (obj_mid + (x0 - obj_mid) / 1.5, obj_y1 + arrow_len / 2.0 - abs(x0 - obj_mid) / 7.0); + pair p2 = (obj_mid+ (x0 - obj_mid) / 50.0, obj_y1); + if (x0 == obj_mid) { + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.7), + gray, + align=-N + W * 0.7 + ), + p0 -- p2, + arrow=Arrow + ); + } else if (x0 < obj_mid) + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.6), + gray, + align=-N + ), + p0{down}..{right}p1{right}..{down}p2, + arrow=Arrow + ); + else + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{left}p1{left}..{down}p2, + arrow=Arrow + ); +} + +void draw_box(real x0, real x1, int i, real y0 = box_y0, real y1 = box_y1, pen color=royalblue) { + draw_arrow_to_obj(x0 + arrow_down_offset, y0); + path ptr = box((x0, y0), (x1, y1)); + fill(ptr, color); + draw(ptr, black); + label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); +} + +void draw_last_arrows() { + int last_index = (int)box_count + 1; + real last_x0 = get_x0(last_index) - arrow_len / 2; + real last_x1 = get_x1(last_index) - arrow_len / 2; + draw_strait_arrows(last_x0, last_x1, arrow_len / 2); +} + + +path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); +fill(obj, deepgreen); +draw(obj, black); +label("$object$", (obj_label_x, obj_label_y), white); - // right arrows: - draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); - draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); - // down arrow: - draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +for (int i = 1; i <= box_count; ++i) { + real x0 = get_x0(i), x1 = get_x1(i); + draw_strait_arrows(x0, x1, i == 1 ? arrow_len / 2 : arrow_len); + draw_box(x0, x1, i); } +draw_last_arrows(); \end{asy} +\label{fig:prob1_6_2} +\end{minipage} -Присваивание \mintinline{c++}{linked_ptr} - это вставка в нужное место двусвязного списка. -\mintinline{c++}{auto ptr4 = ptr2;} +Копирование \mintinline{c++}{linked_ptr} - это вставка в нужное место двусвязного списка. После выполнения операции \mintinline{c++}{auto ptr4 = ptr2;} двусвязный список указателей будет выглядеть следующим образом: +\begin{minipage}[h]{0.49\linewidth} +\centering \begin{asy} +import unicode; +settings.outformat = "pdf"; +texpreamble("\setmainlanguage{english}\setotherlanguage{urdu}\newfontfamily\urdufont{Times New Roman}"); + real arrow_len = 5; -real box_h = arrow_len; -real box_w = 2.0 * box_h; -real box_count = 4; -real object_box_w = box_count * box_w + (box_count + 1) * arrow_len; +real box_h = arrow_len / 3.0; +real box_w = box_h; +real box_count = 3; +real object_box_w = 2 * box_w; + +real get_x0(int i) { + return arrow_len * i + box_w * (i - 1); +} -real obj_x0 = 0; +real get_x1(int i) { + return get_x0(i) + box_w; +} + +real obj_x0 = get_x0(2) - box_w / 2.0; real obj_x1 = obj_x0 + object_box_w; real obj_y0 = 0; real obj_y1 = box_h; +real eps = arrow_len / 10.0; + real obj_label_x = (obj_x0 + obj_x1) / 2.0; real obj_label_y = (obj_y0 + obj_y1) / 2.0; @@ -405,80 +539,233 @@ \subsection{linked\_ptr} real box_label_x_offset = box_w / 2.0; real box_label_y_offset = box_h / 2.0; -real arrow_left_y = box_y0 + box_h * 2.0 / 3.0; -real arrow_right_y = box_y0 + box_h * 1.0 / 3.0; +real arrow_left_offset = box_h * 2.0 / 3.0; +real arrow_right_offset = box_h * 1.0 / 3.0; +real arrow_left_y = box_y0 + arrow_left_offset; +real arrow_right_y = box_y0 + arrow_right_offset; real arrow_down_offset = box_w * 0.5; -size(10cm,0); -// green box: +real label_offset = box_h / 6.0; +size(15cm,0); + +void draw_arrow(path p, real relative_pos, string text, align al) { + draw( + L=Label( + scale(0.5)*("$" + text + "$"), + position=Relative(relative_pos), + gray, + align = al + ), + p, + arrow=Arrow + ); +} + +void draw_strait_arrows(real x0, real x1, real len = arrow_len) { + real rel_pos = 0.75 + 0.22 * (1 - arrow_len / len); + draw_arrow((x0 - len, arrow_left_y) -- (x0, arrow_left_y), rel_pos, "next", (E + N / 2.0)); + draw_arrow((x0, arrow_right_y) -- (x0 - len, arrow_right_y), rel_pos, "prev", (W - N / 2.0)); +} + +void draw_curved_arrow(pair p0, pair p3, string text, bool reversed = false) { + real delta = arrow_len * (1.0 / 3.0); + pair p1 = (xpart(p0) + delta, ypart(p0)); + pair p2 = (xpart(p3) - delta, ypart(p3)); + pair add = (arrow_len / 10.0, 0); + if (reversed) { + p1 -= add; + p2 -= add - (0, box_h / 10.0); + } else { + p1 += add - (0, box_h / 10.0); + p2 += add; + } + p2 += 0.3 * (p1 - p2); + p1 -= 0.3 * (p1 - p2); + pair p1_5 = (p1 + p2) / 2.0; + + //draw(circle(p1, 0.1), red); + //draw(circle(p1_5, 0.1), magenta); + //draw(circle(p2, 0.1), blue); + + path p = p0{right}..{down}p1{down}..{left}p1_5{left}..{down}p2{down}..{right}p3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); + + pair q0 = p3 + box_w, q1 = p2 + (box_w + 2 * (xpart(p3) - xpart(p2)), 0), q3 = p0 + arrow_len, q2 = p1 + (arrow_len - 2 * (xpart(p1) - xpart(p0)), 0); + pair q1_5 = (q1 + q2) / 2.0; + + //draw(circle(q1, 0.1), red); + //draw(circle(q1_5, 0.1), magenta); + //draw(circle(q2, 0.1), blue); + + p = q0{right}..{up}q1{up}..{left}q1_5{left}..{up}q2{up}..{right}q3; + draw_arrow(reversed ? reverse(p) : p, 0.938, text, reversed ? N : -N); +} + +void draw_arrow_to_obj(real x0, real y0) { + pair p0 = (x0, y0); + real obj_mid = (obj_x0 + obj_x1) / 2.0; + pair p1 = (obj_mid + (x0 - obj_mid) / 1.5, obj_y1 + arrow_len / 2.0 - abs(x0 - obj_mid) / 7.0); + pair p2 = (obj_mid+ (x0 - obj_mid) / 50.0, obj_y1); + if (x0 == obj_mid) { + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.6), + gray, + align=-N + W * 0.7 + ), + p0 -- p2, + arrow=Arrow + ); + } else if (x0 < obj_mid) + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{right}p1{right}..{down}p2, + arrow=Arrow + ); + else + draw( + L=Label( + scale(0.5)*"$ptr$", + position=Relative(0.5), + gray, + align=-N + ), + p0{down}..{left}p1{left}..{down}p2, + arrow=Arrow + ); +} + +void draw_box(real x0, real x1, int i, real y0 = box_y0, real y1 = box_y1, pen color=royalblue) { + draw_arrow_to_obj(x0 + arrow_down_offset, y0); + path ptr = box((x0, y0), (x1, y1)); + fill(ptr, color); + draw(ptr, black); + label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); +} + +void draw_last_arrows() { + int last_index = (int)box_count + 1; + real last_x0 = get_x0(last_index) - arrow_len / 2; + real last_x1 = get_x1(last_index) - arrow_len / 2; + draw_strait_arrows(last_x0, last_x1, arrow_len / 2); +} + + path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); -fill(obj, green); -// it's label : -label("$O B J E C T$", (obj_label_x, obj_label_y), white); - -for (int i = 1; i <= box_count; ++i) { - real x0 = arrow_len * i + box_w * (i - 1); - real x1 = x0 + box_w; - - // blue box: - path ptr = box((x0, box_y0), (x1, box_y1)); - fill(ptr, blue); - // it's label: - real nmb = i; - if (i >= 3) { - nmb = 7 - i; - } - label("$ptr" + string(nmb) + "$", (x0 + box_label_x_offset, box_y0 + box_label_y_offset), white); - - // left arrows: - draw((x0 - arrow_len, arrow_left_y) -- (x0, arrow_left_y), arrow=Arrow); - draw((x1, arrow_left_y) -- (x1 + arrow_len, arrow_left_y), arrow = Arrow); - - // right arrows: - draw((x0, arrow_right_y) -- (x0 - arrow_len, arrow_right_y), arrow=Arrow); - draw((x1 + arrow_len, arrow_right_y) -- (x1, arrow_right_y), arrow = Arrow); - - // down arrow: - draw((x0 + arrow_down_offset, box_y0) -- (x0 + arrow_down_offset, box_y0 - arrow_len), arrow=Arrow); +fill(obj, deepgreen); +draw(obj, black); +label("$object$", (obj_label_x, obj_label_y), white); + + +for (int i = 1; i <= 2; ++i) { + real x0 = get_x0(i), x1 = get_x1(i); + draw_strait_arrows(x0, x1, i == 1 ? arrow_len / 2 : arrow_len); + draw_box(x0, x1, i); } +draw_last_arrows(); + +real x2_0 = get_x0(2), x2_1 = get_x1(2), x3_0 = get_x0(3), x3_1 = get_x1(3); +real x4_0 = x2_1 + (arrow_len - box_w) / 2.0; +real x4_1 = x4_0 + box_w; +real y4_0 = box_y0 - arrow_len / 2.0; +real y4_1 = y4_0 + box_h; + +draw_box(x3_0, x3_1, 3); + +draw_box(x4_0, x4_1, 4, y4_0, y4_1, brown); + +draw_curved_arrow((x2_1, arrow_left_y), (x4_0, y4_0 + arrow_left_offset), "next", false); +draw_curved_arrow((x2_1, arrow_right_y), (x4_0, y4_0 + arrow_right_offset), "prev", true); \end{asy} +\end{minipage} +\label{fig:prob1_6_2} +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} +linked_ptr(linked_ptr const& other) noexcept + : ptr(other.ptr) + , prev(other.prev) + , next(other.next) +{ + if (this->prev) + this->prev->next = this; + if (this->next) + this->next->prev = this; +} +\end{minted} +Аналогично, при удалении \mintinline{c++}{linked_ptr} просто перенаправляются ссылки соседей и при необходимости удаляется сам объект: \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -linked_ptr(linked_ptr const& other) noexcept : - prev(other.prev), - next(other.next), - ptr(other.ptr) - {} +~linked_ptr() noexcept +{ + if (!ptr) return; + if (prev == this) + delete ptr; + if (prev) prev->next = next; + if (next) next->prev = prev; +} \end{minted} -Аналогично, при удалении \mintinline{c++}{linked_ptr} просто перенаправляются ссылки соседей и при необходимости удаояется сам объект: +Как видно, в \mintinline{c++}{linked_ptr} все элементы списка одновременно владеют ссылаемым объектом, но при этом имеют право удалять его только при удалении списка. + +Move-конструктор у \mintinline{c++}{linked_ptr} может выглядеть следующим образом: + \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} - -~linked_ptr() noexcept { - if (!ptr) return; - if (prev == this) - delete ptr; - if (prev) prev->next = next; - if (next) next->prev = prev; +linked_ptr(linked_ptr&& other) noexcept + : ptr(other.ptr) + , prev(other.prev) + , next(other.next) +{ + other.prev = other.next = nullptr; + other.ptr = nullptr; + if (this->prev) + this->prev->next = this; + if (this->next) + this->next->prev = this; } +\end{minted} +Заметим, что в отличае от конструктора копирования нужно занулять члены other, потому что на нем будет вызван деструктор. + +Остальной интерфейс \mintinline{c++}{linked_ptr} аналагичен таковому у \mintinline{c++}{shared_ptr}: +\begin{minted}[ +linenos, +frame=lines, +framesep=2mm] +{c++} +T* get() const noexcept { return ptr; } // разыменование указателя +T* operator->() const noexcept { return ptr; } // вызов мембера объекта, на который указывает linked_ptr \end{minted} -Как видно, в \mintinline{c++}{linked_ptr} все элементы списка одновременно владеют ссылаемым объектом, но при этом имеют право удалять его только при удалении списка. +\subsubsection{сравнение \mintinline{c++}{linked_ptr} и \mintinline{c++}{shared_ptr}} -\subsection{intrusive\_ptr} +\begin{enumerate} +\item \mintinline{c++}{std::shared_ptr} аллоцирует 1 вспомогательный объект на хипе, а \mintinline{c++}{linked_ptr} - ничего. +\item \mintinline{c++}{linked_ptr} не может быть размещен в константном пространстве памяти из-за mutable членов next и prev. Так, например, при передаче \mintinline{c++}{const linked_ptr&} в конструктор копирования на самом деле мы меняем эти 2 указателя на соседей, чтобы вставить в нужное место двусвязного списка. +\item \mintinline{c++}{linked_ptr} также имеет внутри больше присваиваний и сравнений на каждую операцию копирования и присваивания, в то время как \mintinline{c++}{std::shared_ptr} должен только инкрементировать счетчик. +\end{enumerate} + +\subsection{\mintinline{c++}{intrusive_ptr}} -\mintinline{c++}{intrusive_ptr} - также разделяемый умный указатель. В stl его нет, но он есть в boost. Идея очень похожа на \mintinline{c++}{std::shared_ptr}, но в отличае от последнего в \mintinline{c++}{intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. Т.е. Реализовать его остается задачей пользователя. +\mintinline{c++}{intrusive_ptr} - также умный указатель с разделяемым владением. В stl его нет, но он есть в boost. Его реализация разделяемого владения очень похожа на \mintinline{c++}{std::shared_ptr}, но в отличае от последнего в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. Т.е. Реализовать его остается задачей пользователя. Для корректной работы \mintinline{c++}{intrusive_ptr} надо реализовать функции: \begin{minted}[ @@ -486,10 +773,8 @@ \subsection{intrusive\_ptr} frame=lines, framesep=2mm] {c++} - int intrusive_ptr_add_ref(T* p); int intrusive_ptr_release(T* p); - \end{minted} Пример использования : @@ -499,7 +784,6 @@ \subsection{intrusive\_ptr} frame=lines, framesep=2mm] {c++} - #include class Test { @@ -517,12 +801,11 @@ \subsection{intrusive\_ptr} } int main() { - boost::intrusive_ptr p(new Test()); + boost::intrusive_ptr p(new Test()); // аллоцированный объект, переданный в конструктор будет удален при выходе из main } - \end{minted} -Замечание: \mintinline{c++}{intrusive_ptr} по понятным причинам эффективнее \mintinline{c++}{shared_ptr}, и если планируется использование разделяемого smart-pointer’а для него, то лучше юзать \mintinline{c++}{intrusive_ptr}, а в случае если приходится иметь дело с библиотечным классом, то \mintinline{c++}{std::shared_ptr}. +Замечание: \mintinline{c++}{intrusive_ptr} по понятным причинам эффективнее \mintinline{c++}{shared_ptr}, и если планируется использование разделяемого smart-pointer’а для класса, реализуемого самим пользователем, то лучше юзать \mintinline{c++}{intrusive_ptr}, а в случае если приходится иметь дело с библиотечным классом, то \mintinline{c++}{std::shared_ptr}. \subsection{make\_shared / make\_unique} @@ -533,12 +816,10 @@ \subsection{make\_shared / make\_unique} frame=lines, framesep=2mm] {c++} - template shared_ptr make_shared(Args&&… args) { - return shared_ptr(new T(std::forward(args)...)); + return shared_ptr(new T(std::forward(args)...)); } - \end{minted} Исторически введение данного метода должно было решить проблему с гарантиями безопасности. Представим следующую ситуацию : @@ -547,9 +828,7 @@ \subsection{make\_shared / make\_unique} frame=lines, framesep=2mm] {c++} - f(std::shared_ptr(new Class(“class”)), g()); - \end{minted} При этом \mintinline{c++}{g()} бросает исключение. Т.к. по стандарту нет гарантии порядка вычисления аргументов \mintinline{c++}{f}, то может произойти следующее : сначала создадим \mintinline{c++}{new Class()}, выделим память, потом - вызовем \mintinline{c++}{g()}, которое бросит исключение, после чего конструктор \mintinline{c++}{shared_ptr} уже не вызовется, т.к. бросился exception. А т.к. мы нигде не пишем слово \mintinline{c++}{delete}, то произойдет memory leak. @@ -561,9 +840,7 @@ \subsection{make\_shared / make\_unique} frame=lines, framesep=2mm] {c++} - f(std::make_shared(“class”), g()); - \end{minted} Если сначала вызовется \mintinline{c++}{g()}, то до конструктора \mintinline{c++}{Class} дело вообще не дойдет и упомянутой проблемы не возникнет. С другой стороны, если сначала вызовется \mintinline{c++}{make_shared}, то объект будет уже обернут в smart pointer и после исключения в \mintinline{c++}{g()} он будет автоматически удален!!! @@ -672,36 +949,198 @@ \subsection{Performance test} Также уместно представить сравнение производительностей разных видов указателей: -% \ASYinputx{g} +\begin{minipage}[h]{0.5\linewidth} +\centering \begin{asy} -real linked = 51; -real shared = 75; -real intrusive = 55; -real unique = 0; -real bench_width = 45.0; -real max_height = 75; - -real linked_offset = bench_width; -real shared_offset = 2 * bench_width; -real intrusive_offset = 3 * bench_width; -real unique_offset = 4 * bench_width; +settings.outformat = "pdf"; + +real linked = 223; +real shared = 557; +real intrusive = 177; +real unique = 222; +real linked_disp = 1; +real shared_disp = 3; +real intrusive_disp = 2; +real unique_disp = 1;real bench_width = 295.0; +real max_height = 557; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; real label_offset = (shared_offset - linked_offset) / 2.0; size(20cm,0); -path lin = box((0,0), (linked_offset,linked)); -fill(lin, green); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("$" + string(linked) + "$"), + (point_offset, linked), + black +); +label( + scale(0.5) * ("$" + string(shared) + "$"), + (point_offset, shared), + black +); +label( + scale(0.5) * ("$" + string(intrusive) + "$"), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("$" + string(unique) + "$"), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, blue); +fill(sha, royalblue); path intr = box((shared_offset,0), (intrusive_offset,intrusive)); fill(intr, magenta); path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, red); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked * 1.03), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared * 1.03), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive * 1.03), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique * 1.03), black); -label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); +label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked + label_delta), black); +label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared + label_delta), black); +label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique + label_delta), black); +label("allocation/deallocation benchmark : ", (bench_width * 2.0, max_height * 1.1)); +real linked = 185; +real shared = 371; +real intrusive = 188; +real unique = 185; +real linked_disp = 2; +real shared_disp = 4; +real intrusive_disp = 1; +real unique_disp = 2;path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, black); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, black); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, black); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, black); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_heigth=bench_width / 4.0; +label("$heap allocation$", (linked_offset - label_offset, label_heigth), white); +label("$heap allocation$", (shared_offset - label_offset, label_heigth), white); +label("$heap allocation$", (intrusive_offset - label_offset, label_heigth), white); +label("$heap allocation$", (unique_offset - label_offset, label_heigth), white); + \end{asy} + +% \begin{figure}[h!] +% \begin{minipage}{1.0\textwidth} +% \centering +% \includegraphics{alloc} +% \label{fig:sub:subfigure1} +% \end{minipage} +% \caption{\textsl{Figure text.}} +% \label{fig:whole_figure} +% \end{figure} + +\end{minipage} + From 8284908e5c8d50137b7778b6da526372c42f3d4b Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Fri, 29 Dec 2017 20:48:19 +0300 Subject: [PATCH 04/18] TODO's fixed --- asy_files/alloc.asy | 34 ++-- asy_files/copy.asy | 18 +- asy_files/move.asy | 26 +-- smart-pointers.tex | 461 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 427 insertions(+), 112 deletions(-) diff --git a/asy_files/alloc.asy b/asy_files/alloc.asy index 1be1d58..9b7eb08 100644 --- a/asy_files/alloc.asy +++ b/asy_files/alloc.asy @@ -1,14 +1,14 @@ settings.outformat = "pdf"; -real linked = 236; -real shared = 604; -real intrusive = 180; -real unique = 225; -real linked_disp = 5; -real shared_disp = 8; -real intrusive_disp = 3; -real unique_disp = 1;real bench_width = 311.0; -real max_height = 604; +real linked = 221; +real shared = 572; +real intrusive = 214; +real unique = 239; +real linked_disp = 0; +real shared_disp = 2; +real intrusive_disp = 8; +real unique_disp = 6;real bench_width = 312.0; +real max_height = 572; real arrow_offset = bench_width / 4.0 * 1.1; real point_offset = arrow_offset * 2 / 3; @@ -105,14 +105,14 @@ label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("allocation/deallocation benchmark : ", (bench_width * 2.0, max_height * 1.1)); -real linked = 183; -real shared = 366; -real intrusive = 181; -real unique = 183; -real linked_disp = 1; -real shared_disp = 3; -real intrusive_disp = 0; -real unique_disp = 1;path lin = box((arrow_offset,0), (linked_offset,linked)); +real linked = 178; +real shared = 357; +real intrusive = 195; +real unique = 178; +real linked_disp = 4; +real shared_disp = 9; +real intrusive_disp = 3; +real unique_disp = 4;path lin = box((arrow_offset,0), (linked_offset,linked)); fill(lin, black); path sha = box((linked_offset,0), (shared_offset,shared)); diff --git a/asy_files/copy.asy b/asy_files/copy.asy index 62d9441..9e55cc6 100644 --- a/asy_files/copy.asy +++ b/asy_files/copy.asy @@ -1,14 +1,14 @@ settings.outformat = "pdf"; -real linked = 9; -real shared = 47; +real linked = 12; +real shared = 48; real intrusive = 10; real unique = 0; real linked_disp = 0; real shared_disp = 0; real intrusive_disp = 0; -real unique_disp = 0;real bench_width = 16.0; -real max_height = 47; +real unique_disp = 0;real bench_width = 18.0; +real max_height = 48; real arrow_offset = bench_width / 4.0 * 1.1; real point_offset = arrow_offset * 2 / 3; @@ -99,9 +99,9 @@ draw( -- (intrusive_offset, unique - unique_disp) , gray); -real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); -label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); -label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); -label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); -label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/asy_files/move.asy b/asy_files/move.asy index 0017128..af5eeae 100644 --- a/asy_files/move.asy +++ b/asy_files/move.asy @@ -1,14 +1,14 @@ settings.outformat = "pdf"; -real linked = 7; -real shared = 2; -real intrusive = 2; -real unique = 2; +real linked = 36; +real shared = 5; +real intrusive = 4; +real unique = 4; real linked_disp = 0; real shared_disp = 0; real intrusive_disp = 0; -real unique_disp = 0;real bench_width = 3.0; -real max_height = 7; +real unique_disp = 0;real bench_width = 12.0; +real max_height = 36; real arrow_offset = bench_width / 4.0 * 1.1; real point_offset = arrow_offset * 2 / 3; @@ -89,9 +89,9 @@ draw( , gray); draw( (intrusive_offset, unique) - -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) -- (intrusive_offset + disp_eps, unique + unique_disp) - -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) -- (intrusive_offset, unique + unique_disp) -- (intrusive_offset, unique - unique_disp) -- (intrusive_offset + disp_eps, unique - unique_disp) @@ -99,9 +99,9 @@ draw( -- (intrusive_offset, unique - unique_disp) , gray); -real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); -label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); -label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); -label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); -label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); diff --git a/smart-pointers.tex b/smart-pointers.tex index 73b582d..78d27ae 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -325,17 +325,13 @@ \subsection{\mintinline{c++}{linked_ptr}} mutable linked_ptr* next; }; \end{minted} -Члены prev и next помечены как mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2. +Члены prev и next помечены как \mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2. Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : \begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} -import unicode; -settings.outformat = "pdf"; -texpreamble("\setmainlanguage{english}\setotherlanguage{urdu}\newfontfamily\urdufont{Times New Roman}"); - real arrow_len = 5; real box_h = arrow_len / 3.0; real box_w = box_h; @@ -500,15 +496,11 @@ \subsection{\mintinline{c++}{linked_ptr}} \label{fig:prob1_6_2} \end{minipage} -Копирование \mintinline{c++}{linked_ptr} - это вставка в нужное место двусвязного списка. После выполнения операции \mintinline{c++}{auto ptr4 = ptr2;} двусвязный список указателей будет выглядеть следующим образом: +При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: \begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} -import unicode; -settings.outformat = "pdf"; -texpreamble("\setmainlanguage{english}\setotherlanguage{urdu}\newfontfamily\urdufont{Times New Roman}"); - real arrow_len = 5; real box_h = arrow_len / 3.0; real box_w = box_h; @@ -692,17 +684,19 @@ \subsection{\mintinline{c++}{linked_ptr}} {c++} linked_ptr(linked_ptr const& other) noexcept : ptr(other.ptr) - , prev(other.prev) - , next(other.next) { - if (this->prev) - this->prev->next = this; - if (this->next) - this->next->prev = this; + if (ptr == nullptr) { + prev = next = nullptr; + return; + } + prev = &other; + next = other.next; + prev->next = this; + next->prev = this; } \end{minted} -Аналогично, при удалении \mintinline{c++}{linked_ptr} просто перенаправляются ссылки соседей и при необходимости удаляется сам объект: +При удалении \mintinline{c++}{linked_ptr} он удаляется из двусвязного списка. Если он был единственным элементом списка то он удаляет сам объект: \begin{minted}[ linenos, frame=lines, @@ -720,8 +714,6 @@ \subsection{\mintinline{c++}{linked_ptr}} \end{minted} -Как видно, в \mintinline{c++}{linked_ptr} все элементы списка одновременно владеют ссылаемым объектом, но при этом имеют право удалять его только при удалении списка. - Move-конструктор у \mintinline{c++}{linked_ptr} может выглядеть следующим образом: \begin{minted}[ @@ -744,7 +736,7 @@ \subsection{\mintinline{c++}{linked_ptr}} \end{minted} Заметим, что в отличае от конструктора копирования нужно занулять члены other, потому что на нем будет вызван деструктор. -Остальной интерфейс \mintinline{c++}{linked_ptr} аналагичен таковому у \mintinline{c++}{shared_ptr}: +\mintinline{c++}{linked_ptr} реализует операции \mintinline{c++}{get()}, \mintinline{c++}{operator*} и \mintinline{c++}{operator->}, как и другие умные указатели: \begin{minted}[ linenos, @@ -753,20 +745,23 @@ \subsection{\mintinline{c++}{linked_ptr}} {c++} T* get() const noexcept { return ptr; } // разыменование указателя T* operator->() const noexcept { return ptr; } // вызов мембера объекта, на который указывает linked_ptr +T& operator*() const { return *ptr; } \end{minted} \subsubsection{сравнение \mintinline{c++}{linked_ptr} и \mintinline{c++}{shared_ptr}} \begin{enumerate} -\item \mintinline{c++}{std::shared_ptr} аллоцирует 1 вспомогательный объект на хипе, а \mintinline{c++}{linked_ptr} - ничего. -\item \mintinline{c++}{linked_ptr} не может быть размещен в константном пространстве памяти из-за mutable членов next и prev. Так, например, при передаче \mintinline{c++}{const linked_ptr&} в конструктор копирования на самом деле мы меняем эти 2 указателя на соседей, чтобы вставить в нужное место двусвязного списка. +\item \mintinline{c++}{std::shared_ptr} при передаче в конструктор указателя на объект аллоцирует еще 1 вспомогательный объект на хипе, а \mintinline{c++}{linked_ptr} - ничего. +\item \mintinline{c++}{linked_ptr} имеет больший размер, чем \mintinline{c++}{shared_ptr} --- три указателя, по сравненею с двумя у \mintinline{c++}{shared_ptr}. +\item \mintinline{c++}{std::shared_ptr} в стандартной библиотеке является thread-safe. Он использует атомарные операции инкремента и декремента для счетчика ссылок. Thread-safe \mintinline{c++}{linked_ptr} не может быть так просто реализован и требует использования уже thread-safe структур данных. \item \mintinline{c++}{linked_ptr} также имеет внутри больше присваиваний и сравнений на каждую операцию копирования и присваивания, в то время как \mintinline{c++}{std::shared_ptr} должен только инкрементировать счетчик. \end{enumerate} \subsection{\mintinline{c++}{intrusive_ptr}} -\mintinline{c++}{intrusive_ptr} - также умный указатель с разделяемым владением. В stl его нет, но он есть в boost. Его реализация разделяемого владения очень похожа на \mintinline{c++}{std::shared_ptr}, но в отличае от последнего в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. Т.е. Реализовать его остается задачей пользователя. -Для корректной работы \mintinline{c++}{intrusive_ptr} надо реализовать функции: +\mintinline{c++}{intrusive_ptr} - также умный указатель с разделяемым владением, реализованный в библиотеке Boost. Так же как и \mintinline{c++}{std::shared_ptr}, он использует подсчет ссылок, но в отличии от \mintinline{c++}{std::shared_ptr} в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. + +Для использования \mintinline{c++}{intrusive_ptr} требуется реализовать функции: \begin{minted}[ linenos, @@ -777,7 +772,7 @@ \subsection{\mintinline{c++}{intrusive_ptr}} int intrusive_ptr_release(T* p); \end{minted} -Пример использования : +Пример использования: \begin{minted}[ linenos, @@ -805,24 +800,23 @@ \subsection{\mintinline{c++}{intrusive_ptr}} } \end{minted} -Замечание: \mintinline{c++}{intrusive_ptr} по понятным причинам эффективнее \mintinline{c++}{shared_ptr}, и если планируется использование разделяемого smart-pointer’а для класса, реализуемого самим пользователем, то лучше юзать \mintinline{c++}{intrusive_ptr}, а в случае если приходится иметь дело с библиотечным классом, то \mintinline{c++}{std::shared_ptr}. +Замечание: \mintinline{c++}{boost::intrusive_ptr} эффективнее \mintinline{c++}{std::shared_ptr}, поскольку пользователь сам может встроить эффективный счетчик ссылок для своего объекта. Причем контракт функций \mintinline{c++}{intrusive_ptr_add_ref} и \mintinline{c++}{intrusive_ptr_release} такой, что они не накладывают строгие ограничения на счетчик ссылок, кроме того, что \mintinline{c++}{intrusive_ptr_release} должен удалить объект, когда удаляется последний его владелец. Поэтому если планируется использование разделяемого smart-pointer’а для класса, реализуемого самим пользователем, то лучше юзать \mintinline{c++}{intrusive_ptr}, а в случае если приходится иметь дело с библиотечным классом, то \mintinline{c++}{std::shared_ptr}. \subsection{make\_shared / make\_unique} -Как было сказано ранее, помимо стандартных конструкторов от указателя стандарт предлагает фабрики для создания объектов, определенные следующим образом (рассмотрим на примере \mintinline{c++}{std::shared_ptr}) : +Как было сказано ранее, помимо стандартных конструкторов от указателя стандарт предлагает фабрики для создания объектов, определенные следующим образом (рассмотрим на примере \mintinline{c++}{std::shared_ptr}): \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -template -shared_ptr make_shared(Args&&… args) { - return shared_ptr(new T(std::forward(args)...)); -} +template +shared_ptr make_shared(Args&&... args); // создает std::shared_ptr, указывающий на объект T(args...) \end{minted} +Исторически введение данного метода должно было решить проблему производительности \mintinline{c++}{std::shared_ptr}, аллоцируя внутри только один объект, содержащий одновременно и счетчик ссылок и объект типа T. Причем при вызове mintinline{c++}{std::make_shared(args...)} на новом объекте типа T сразу вызовется конструктор с переданными аргументами. Таким образом такой конструктор-фабрика экономит аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new T(args...)} и \mintinline{c++}{new counter()} происходит одна аллокация данных для пары (\mintinline{c++}{object}, \mintinline{c++}{counter}). -Исторически введение данного метода должно было решить проблему с гарантиями безопасности. Представим следующую ситуацию : +Также введение такой функции в стандарт позволило решить проблему с гарантиями безопасности. Представим следующую ситуацию : \begin{minted}[ linenos, frame=lines, @@ -845,16 +839,11 @@ \subsection{make\_shared / make\_unique} Если сначала вызовется \mintinline{c++}{g()}, то до конструктора \mintinline{c++}{Class} дело вообще не дойдет и упомянутой проблемы не возникнет. С другой стороны, если сначала вызовется \mintinline{c++}{make_shared}, то объект будет уже обернут в smart pointer и после исключения в \mintinline{c++}{g()} он будет автоматически удален!!! -Другие приятные особенности фабрик вида \mintinline{c++}{make_pointer} : - -\begin{enumerate} -\item Они экономят аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new object()} и \mintinline{c++}{new counter}() происходит одна аллокация данных для пары <\mintinline{c++}{object}, \mintinline{c++}{counter}> -\item Мы теперь не используем в коде не только \mintinline{c++}{delete}, но и голый \mintinline{c++}{new}. (no naked new) -\end{enumerate} +Начиная с c++14 аналогичная функция-фабрика \mintinline{c++}{std::make_unique} была добавлена и для \mintinline{c++}{std::unique_ptr}. Это позволило не использовать в коде не только \mintinline{c++}{delete}, но и голый \mintinline{c++}{new} (no naked new) для обоих видов умных указателей, включенных в стандартную библиотеку. \subsection{Smart pointers pointing on this} -Особый случай, когда нельзя просто так взять и использовать умные указатели бездумно - это указатели на this, которые возвращает метод класса. Рассмотрим код : +Особый случай, когда нельзя просто так взять и использовать умные указатели бездумно --- это указатели на this, которые возвращает метод класса. Рассмотрим код : \begin{minted}[ linenos, @@ -952,17 +941,15 @@ \subsection{Performance test} \begin{minipage}[h]{0.5\linewidth} \centering \begin{asy} -settings.outformat = "pdf"; - -real linked = 223; -real shared = 557; -real intrusive = 177; -real unique = 222; -real linked_disp = 1; -real shared_disp = 3; -real intrusive_disp = 2; -real unique_disp = 1;real bench_width = 295.0; -real max_height = 557; +real linked = 221; +real shared = 572; +real intrusive = 214; +real unique = 239; +real linked_disp = 0; +real shared_disp = 2; +real intrusive_disp = 8; +real unique_disp = 6;real bench_width = 312.0; +real max_height = 572; real arrow_offset = bench_width / 4.0 * 1.1; real point_offset = arrow_offset * 2 / 3; @@ -976,22 +963,22 @@ \subsection{Performance test} draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); label( - scale(0.5) * ("$" + string(linked) + "$"), + scale(0.5) * ("" + string(linked) + ""), (point_offset, linked), black ); label( - scale(0.5) * ("$" + string(shared) + "$"), + scale(0.5) * ("" + string(shared) + ""), (point_offset, shared), black ); label( - scale(0.5) * ("$" + string(intrusive) + "$"), + scale(0.5) * ("" + string(intrusive) + ""), (point_offset, intrusive), black ); label( - scale(0.5) * ("$" + string(unique) + "$"), + scale(0.5) * ("" + string(unique) + ""), (point_offset, unique), black ); @@ -1053,20 +1040,20 @@ \subsection{Performance test} -- (intrusive_offset, unique - unique_disp) , gray); -real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); -label("$linked\underline{\hspace{0.3cm}}ptr$", (linked_offset - label_offset, linked + label_delta), black); -label("$std::shared\underline{\hspace{0.3cm}}ptr$", (shared_offset - label_offset, shared + label_delta), black); -label("$boost::intruisive\underline{\hspace{0.3cm}}ptr$", (intrusive_offset - label_offset, intrusive + label_delta), black); -label("$std::unique\underline{\hspace{0.3cm}}ptr$", (unique_offset - label_offset, unique + label_delta), black); +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("allocation/deallocation benchmark : ", (bench_width * 2.0, max_height * 1.1)); -real linked = 185; -real shared = 371; -real intrusive = 188; -real unique = 185; -real linked_disp = 2; -real shared_disp = 4; -real intrusive_disp = 1; -real unique_disp = 2;path lin = box((arrow_offset,0), (linked_offset,linked)); +real linked = 178; +real shared = 357; +real intrusive = 195; +real unique = 178; +real linked_disp = 4; +real shared_disp = 9; +real intrusive_disp = 3; +real unique_disp = 4;path lin = box((arrow_offset,0), (linked_offset,linked)); fill(lin, black); path sha = box((linked_offset,0), (shared_offset,shared)); @@ -1125,12 +1112,342 @@ \subsection{Performance test} , gray); real label_heigth=bench_width / 4.0; -label("$heap allocation$", (linked_offset - label_offset, label_heigth), white); -label("$heap allocation$", (shared_offset - label_offset, label_heigth), white); -label("$heap allocation$", (intrusive_offset - label_offset, label_heigth), white); -label("$heap allocation$", (unique_offset - label_offset, label_heigth), white); +label("heap allocation", (linked_offset - label_offset, label_heigth), white); +label("heap allocation", (shared_offset - label_offset, label_heigth), white); +label("heap allocation", (intrusive_offset - label_offset, label_heigth), white); +label("heap allocation", (unique_offset - label_offset, label_heigth), white); +\end{asy} +\end{minipage} + +\begin{minipage} +\begin{asy} +real linked = 12; +real shared = 48; +real intrusive = 10; +real unique = 0; +real linked_disp = 0; +real shared_disp = 0; +real intrusive_disp = 0; +real unique_disp = 0;real bench_width = 18.0; +real max_height = 48; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, royalblue); +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); +label("copy constructor benchmark : ", (bench_width * 2.0, max_height * 1.1)); \end{asy} +\end{minipage} + +\begin{minipage} +\begin{asy} +real linked = 36; +real shared = 5; +real intrusive = 4; +real unique = 4; +real linked_disp = 0; +real shared_disp = 0; +real intrusive_disp = 0; +real unique_disp = 0;real bench_width = 12.0; +real max_height = 36; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, royalblue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.05 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta), black); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta), black); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); +label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); +\end{asy} +\end{minipage} + +\begin{minipage} +\begin{asy} +real linked = 1187; +real shared = 3527; +real intrusive = 996; +real unique = 786; +real linked_disp = 5; +real shared_disp = 51; +real intrusive_disp = 18; +real unique_disp = 12;real bench_width = 1624.0; +real max_height = 3527; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + gray +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + gray +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + gray +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + gray +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, royalblue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) +, gray); +draw( + (shared_offset, intrusive) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset + disp_eps, intrusive + intrusive_disp) + -- (shared_offset - disp_eps, intrusive + intrusive_disp) + -- (shared_offset, intrusive + intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) + -- (shared_offset + disp_eps, intrusive - intrusive_disp) + -- (shared_offset - disp_eps, intrusive - intrusive_disp) + -- (shared_offset, intrusive - intrusive_disp) +, gray); +draw( + (intrusive_offset, unique) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset + disp_eps, unique + unique_disp) + -- (intrusive_offset - disp_eps, unique + unique_disp) + -- (intrusive_offset, unique + unique_disp) + -- (intrusive_offset, unique - unique_disp) + -- (intrusive_offset + disp_eps, unique - unique_disp) + -- (intrusive_offset - disp_eps, unique - unique_disp) + -- (intrusive_offset, unique - unique_disp) +, gray); + +real label_delta = 0.03 * max(max(linked, shared), max(intrusive, unique)); +label("linked\underline{\hspace{0.3cm}}ptr", (linked_offset - label_offset, linked + label_delta)); +label("std::shared\underline{\hspace{0.3cm}}ptr", (shared_offset - label_offset, shared + label_delta)); +label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta)); +label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta)); +label("real usecase (decart treee) benchmark : ", (bench_width * 2.0, max_height * 1.1)); +\end{asy} +\end{minipage} % \begin{figure}[h!] % \begin{minipage}{1.0\textwidth} @@ -1142,5 +1459,3 @@ \subsection{Performance test} % \label{fig:whole_figure} % \end{figure} -\end{minipage} - From 11cc0948a2364a87401358d0bb0469eeb01b18d1 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 15:54:18 +0300 Subject: [PATCH 05/18] new_comm --- smart-pointers.tex | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 78d27ae..534245b 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -332,6 +332,7 @@ \subsection{\mintinline{c++}{linked_ptr}} \begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} + real arrow_len = 5; real box_h = arrow_len / 3.0; real box_w = box_h; @@ -374,7 +375,7 @@ \subsection{\mintinline{c++}{linked_ptr}} void draw_arrow(path p, real relative_pos, string text, align al) { draw( L=Label( - scale(0.5)*("$" + text + "$"), + scale(0.5)*("" + text + ""), position=Relative(relative_pos), gray, align = al @@ -432,7 +433,7 @@ \subsection{\mintinline{c++}{linked_ptr}} if (x0 == obj_mid) { draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.7), gray, align=-N + W * 0.7 @@ -443,7 +444,7 @@ \subsection{\mintinline{c++}{linked_ptr}} } else if (x0 < obj_mid) draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.6), gray, align=-N @@ -454,7 +455,7 @@ \subsection{\mintinline{c++}{linked_ptr}} else draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.5), gray, align=-N @@ -469,7 +470,7 @@ \subsection{\mintinline{c++}{linked_ptr}} path ptr = box((x0, y0), (x1, y1)); fill(ptr, color); draw(ptr, black); - label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); + label("ptr" + string(i) + "", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); } void draw_last_arrows() { @@ -483,7 +484,7 @@ \subsection{\mintinline{c++}{linked_ptr}} path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); fill(obj, deepgreen); draw(obj, black); -label("$object$", (obj_label_x, obj_label_y), white); +label("object", (obj_label_x, obj_label_y), white); for (int i = 1; i <= box_count; ++i) { @@ -543,7 +544,7 @@ \subsection{\mintinline{c++}{linked_ptr}} void draw_arrow(path p, real relative_pos, string text, align al) { draw( L=Label( - scale(0.5)*("$" + text + "$"), + scale(0.5)*("" + text + ""), position=Relative(relative_pos), gray, align = al @@ -601,7 +602,7 @@ \subsection{\mintinline{c++}{linked_ptr}} if (x0 == obj_mid) { draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.6), gray, align=-N + W * 0.7 @@ -612,7 +613,7 @@ \subsection{\mintinline{c++}{linked_ptr}} } else if (x0 < obj_mid) draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.5), gray, align=-N @@ -623,7 +624,7 @@ \subsection{\mintinline{c++}{linked_ptr}} else draw( L=Label( - scale(0.5)*"$ptr$", + scale(0.5)*"ptr", position=Relative(0.5), gray, align=-N @@ -638,7 +639,7 @@ \subsection{\mintinline{c++}{linked_ptr}} path ptr = box((x0, y0), (x1, y1)); fill(ptr, color); draw(ptr, black); - label("$ptr" + string(i) + "$", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); + label("ptr" + string(i) + "", (x0 + box_label_x_offset, y0 + box_label_y_offset), white); } void draw_last_arrows() { @@ -652,7 +653,7 @@ \subsection{\mintinline{c++}{linked_ptr}} path obj = box((obj_x0,obj_y0), (obj_x1,obj_y1)); fill(obj, deepgreen); draw(obj, black); -label("$object$", (obj_label_x, obj_label_y), white); +label("object", (obj_label_x, obj_label_y), white); for (int i = 1; i <= 2; ++i) { @@ -674,6 +675,10 @@ \subsection{\mintinline{c++}{linked_ptr}} draw_curved_arrow((x2_1, arrow_left_y), (x4_0, y4_0 + arrow_left_offset), "next", false); draw_curved_arrow((x2_1, arrow_right_y), (x4_0, y4_0 + arrow_right_offset), "prev", true); + +//draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); +//draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); + \end{asy} \end{minipage} \label{fig:prob1_6_2} From f49332602f2a1a89369bdc9dfd39bae8d034ed49 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:00:19 +0300 Subject: [PATCH 06/18] slash --- smart-pointers.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 534245b..2af8d7f 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -819,7 +819,7 @@ \subsection{make\_shared / make\_unique} template shared_ptr make_shared(Args&&... args); // создает std::shared_ptr, указывающий на объект T(args...) \end{minted} -Исторически введение данного метода должно было решить проблему производительности \mintinline{c++}{std::shared_ptr}, аллоцируя внутри только один объект, содержащий одновременно и счетчик ссылок и объект типа T. Причем при вызове mintinline{c++}{std::make_shared(args...)} на новом объекте типа T сразу вызовется конструктор с переданными аргументами. Таким образом такой конструктор-фабрика экономит аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new T(args...)} и \mintinline{c++}{new counter()} происходит одна аллокация данных для пары (\mintinline{c++}{object}, \mintinline{c++}{counter}). +Исторически введение данного метода должно было решить проблему производительности \mintinline{c++}{std::shared_ptr}, аллоцируя внутри только один объект, содержащий одновременно и счетчик ссылок и объект типа T. Причем при вызове \mintinline{c++}{std::make_shared(args...)} на новом объекте типа T сразу вызовется конструктор с переданными аргументами. Таким образом такой конструктор-фабрика экономит аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new T(args...)} и \mintinline{c++}{new counter()} происходит одна аллокация данных для пары (\mintinline{c++}{object}, \mintinline{c++}{counter}). Также введение такой функции в стандарт позволило решить проблему с гарантиями безопасности. Представим следующую ситуацию : \begin{minted}[ From 6d51784759a0bf167e833bc4965c40c4777f9b02 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:15:17 +0300 Subject: [PATCH 07/18] last --- smart-pointers.tex | 96 +++++----------------------------------------- 1 file changed, 10 insertions(+), 86 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 2af8d7f..01cff14 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -329,10 +329,9 @@ \subsection{\mintinline{c++}{linked_ptr}} Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.49\linewidth}[h]{0.49\linewidth} \centering \begin{asy} - real arrow_len = 5; real box_h = arrow_len / 3.0; real box_w = box_h; @@ -499,7 +498,7 @@ \subsection{\mintinline{c++}{linked_ptr}} При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.49\linewidth}[h]{0.49\linewidth} \centering \begin{asy} real arrow_len = 5; @@ -678,10 +677,10 @@ \subsection{\mintinline{c++}{linked_ptr}} //draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); //draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); - +\label{fig:prob1_6_2} \end{asy} \end{minipage} -\label{fig:prob1_6_2} + \begin{minted}[ linenos, frame=lines, @@ -943,7 +942,7 @@ \subsection{Performance test} Также уместно представить сравнение производительностей разных видов указателей: -\begin{minipage}[h]{0.5\linewidth} +\begin{minipage}[h]{0.49\linewidth}[h]{0.5\linewidth} \centering \begin{asy} real linked = 221; @@ -1124,7 +1123,7 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage} +\begin{minipage}[h]{0.49\linewidth} \begin{asy} real linked = 12; real shared = 48; @@ -1234,83 +1233,8 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage} -\begin{asy} -real linked = 36; -real shared = 5; -real intrusive = 4; -real unique = 4; -real linked_disp = 0; -real shared_disp = 0; -real intrusive_disp = 0; -real unique_disp = 0;real bench_width = 12.0; -real max_height = 36; - -real arrow_offset = bench_width / 4.0 * 1.1; -real point_offset = arrow_offset * 2 / 3; -real linked_offset = arrow_offset + bench_width; -real shared_offset = arrow_offset + 2 * bench_width; -real intrusive_offset = arrow_offset + 3 * bench_width; -real unique_offset = arrow_offset + 4 * bench_width; -real label_offset = (shared_offset - linked_offset) / 2.0; - -size(20cm,0); -draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); - -label( - scale(0.5) * ("" + string(linked) + ""), - (point_offset, linked), - black -); -label( - scale(0.5) * ("" + string(shared) + ""), - (point_offset, shared), - black -); -label( - scale(0.5) * ("" + string(intrusive) + ""), - (point_offset, intrusive), - black -); -label( - scale(0.5) * ("" + string(unique) + ""), - (point_offset, unique), - black -); -path lin = box((arrow_offset,0), (linked_offset,linked)); -fill(lin, deepgreen); - -path sha = box((linked_offset,0), (shared_offset,shared)); -fill(sha, royalblue); - -path intr = box((shared_offset,0), (intrusive_offset,intrusive)); -fill(intr, magenta); - -path uniq = box((intrusive_offset,0), (unique_offset,unique)); -fill(uniq, heavyred); - -real disp_eps = bench_width / 20.0; -draw( - (arrow_offset, linked) - -- (arrow_offset, linked + linked_disp) - -- (arrow_offset + disp_eps, linked + linked_disp) - -- (arrow_offset - disp_eps, linked + linked_disp) - -- (arrow_offset, linked + linked_disp) - -- (arrow_offset, linked - linked_disp) - -- (arrow_offset + disp_eps, linked - linked_disp) - -- (arrow_offset - disp_eps, linked - linked_disp) - -- (arrow_offset, linked - linked_disp) -, gray); -draw( - (linked_offset, shared) - -- (linked_offset, shared + shared_disp) - -- (linked_offset + disp_eps, shared + shared_disp) - -- (linked_offset - disp_eps, shared + shared_disp) - -- (linked_offset, shared + shared_disp) - -- (linked_offset, shared - shared_disp) - -- (linked_offset + disp_eps, shared - shared_disp) - -- (linked_offset - disp_eps, shared - shared_disp) - -- (linked_offset, shared - shared_disp) +\begin{ +inked_offset, shared - shared_disp) , gray); draw( (shared_offset, intrusive) @@ -1344,7 +1268,7 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage} +\begin{minipage}[h]{0.49\linewidth} \begin{asy} real linked = 1187; real shared = 3527; @@ -1455,7 +1379,7 @@ \subsection{Performance test} \end{minipage} % \begin{figure}[h!] -% \begin{minipage}{1.0\textwidth} +% \begin{minipage}[h]{0.49\linewidth}{1.0\textwidth} % \centering % \includegraphics{alloc} % \label{fig:sub:subfigure1} From 054cddc3f79cf68e74f3b3353fb767ba06fa4298 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:18:03 +0300 Subject: [PATCH 08/18] last2 --- smart-pointers.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 01cff14..e81d988 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -329,7 +329,7 @@ \subsection{\mintinline{c++}{linked_ptr}} Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : -\begin{minipage}[h]{0.49\linewidth}[h]{0.49\linewidth} +\begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} real arrow_len = 5; @@ -498,7 +498,7 @@ \subsection{\mintinline{c++}{linked_ptr}} При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: -\begin{minipage}[h]{0.49\linewidth}[h]{0.49\linewidth} +\begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} real arrow_len = 5; @@ -942,7 +942,7 @@ \subsection{Performance test} Также уместно представить сравнение производительностей разных видов указателей: -\begin{minipage}[h]{0.49\linewidth}[h]{0.5\linewidth} +\begin{minipage}[h]{0.49\linewidth} \centering \begin{asy} real linked = 221; From 8e4bc4a6a4672cab5af518a098a4939ccb6bf321 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:21:12 +0300 Subject: [PATCH 09/18] last2 --- smart-pointers.tex | 80 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index e81d988..136f224 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -1233,8 +1233,83 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{ -inked_offset, shared - shared_disp) +\begin{minipage}[h]{0.49\linewidth} +\begin{asy} +real linked = 36; +real shared = 5; +real intrusive = 4; +real unique = 4; +real linked_disp = 0; +real shared_disp = 0; +real intrusive_disp = 0; +real unique_disp = 0;real bench_width = 12.0; +real max_height = 36; + +real arrow_offset = bench_width / 4.0 * 1.1; +real point_offset = arrow_offset * 2 / 3; +real linked_offset = arrow_offset + bench_width; +real shared_offset = arrow_offset + 2 * bench_width; +real intrusive_offset = arrow_offset + 3 * bench_width; +real unique_offset = arrow_offset + 4 * bench_width; +real label_offset = (shared_offset - linked_offset) / 2.0; + +size(20cm,0); +draw((arrow_offset / 2, 0) -- (arrow_offset / 2, max_height),arrow=Arrow); + +label( + scale(0.5) * ("" + string(linked) + ""), + (point_offset, linked), + black +); +label( + scale(0.5) * ("" + string(shared) + ""), + (point_offset, shared), + black +); +label( + scale(0.5) * ("" + string(intrusive) + ""), + (point_offset, intrusive), + black +); +label( + scale(0.5) * ("" + string(unique) + ""), + (point_offset, unique), + black +); +path lin = box((arrow_offset,0), (linked_offset,linked)); +fill(lin, deepgreen); + +path sha = box((linked_offset,0), (shared_offset,shared)); +fill(sha, royalblue); + +path intr = box((shared_offset,0), (intrusive_offset,intrusive)); +fill(intr, magenta); + +path uniq = box((intrusive_offset,0), (unique_offset,unique)); +fill(uniq, heavyred); + +real disp_eps = bench_width / 20.0; +draw( + (arrow_offset, linked) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset + disp_eps, linked + linked_disp) + -- (arrow_offset - disp_eps, linked + linked_disp) + -- (arrow_offset, linked + linked_disp) + -- (arrow_offset, linked - linked_disp) + -- (arrow_offset + disp_eps, linked - linked_disp) + -- (arrow_offset - disp_eps, linked - linked_disp) + -- (arrow_offset, linked - linked_disp) +, gray); +draw( + (linked_offset, shared) + -- (linked_offset, shared + shared_disp) + -- (linked_offset + disp_eps, shared + shared_disp) + -- (linked_offset - disp_eps, shared + shared_disp) + -- (linked_offset, shared + shared_disp) + -- (linked_offset, shared - shared_disp) + -- (linked_offset + disp_eps, shared - shared_disp) + -- (linked_offset - disp_eps, shared - shared_disp) + -- (linked_offset, shared - shared_disp) , gray); draw( (shared_offset, intrusive) @@ -1265,6 +1340,7 @@ \subsection{Performance test} label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); + \end{asy} \end{minipage} From 18d0dee67206c5f7f8bd123d1840c56cfb5fcf88 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:23:49 +0300 Subject: [PATCH 10/18] last2 --- smart-pointers.tex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 136f224..944c6d0 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -493,7 +493,7 @@ \subsection{\mintinline{c++}{linked_ptr}} } draw_last_arrows(); \end{asy} -\label{fig:prob1_6_2} +% \label{fig:prob1_6_2} \end{minipage} При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: @@ -677,7 +677,7 @@ \subsection{\mintinline{c++}{linked_ptr}} //draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); //draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); -\label{fig:prob1_6_2} +% \label{fig:prob1_6_3} \end{asy} \end{minipage} @@ -1340,7 +1340,6 @@ \subsection{Performance test} label("boost::intruisive\underline{\hspace{0.3cm}}ptr", (intrusive_offset - label_offset, intrusive + label_delta), black); label("std::unique\underline{\hspace{0.3cm}}ptr", (unique_offset - label_offset, unique + label_delta), black); label("std::move benchmark : ", (bench_width * 2.0, max_height * 1.1)); - \end{asy} \end{minipage} From beed4c74192c9e5b016d20ea141ddfbdc9e36760 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:24:56 +0300 Subject: [PATCH 11/18] last2 --- smart-pointers.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 944c6d0..13e8c07 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -677,8 +677,8 @@ \subsection{\mintinline{c++}{linked_ptr}} //draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); //draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); -% \label{fig:prob1_6_3} \end{asy} +% \label{fig:prob1_6_3} \end{minipage} \begin{minted}[ From 86bdb24af330a7a028154040fc390891c385bb30 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:28:51 +0300 Subject: [PATCH 12/18] last2 --- smart-pointers.tex | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 13e8c07..1201613 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -329,7 +329,7 @@ \subsection{\mintinline{c++}{linked_ptr}} Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \centering \begin{asy} real arrow_len = 5; @@ -493,12 +493,12 @@ \subsection{\mintinline{c++}{linked_ptr}} } draw_last_arrows(); \end{asy} -% \label{fig:prob1_6_2} +\label{fig:prob1_6_2} \end{minipage} При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \centering \begin{asy} real arrow_len = 5; @@ -678,7 +678,7 @@ \subsection{\mintinline{c++}{linked_ptr}} //draw_curved_arrow((x4_1, y4_0 + arrow_left_offset), (x3_0, arrow_left_y), "next", false); //draw_curved_arrow((x4_1, y4_0 + arrow_right_offset), (x3_0, arrow_right_y), "prev", true); \end{asy} -% \label{fig:prob1_6_3} +\label{fig:prob1_6_2} \end{minipage} \begin{minted}[ @@ -942,7 +942,7 @@ \subsection{Performance test} Также уместно представить сравнение производительностей разных видов указателей: -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \centering \begin{asy} real linked = 221; @@ -1123,7 +1123,7 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \begin{asy} real linked = 12; real shared = 48; @@ -1233,7 +1233,7 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \begin{asy} real linked = 36; real shared = 5; @@ -1343,7 +1343,7 @@ \subsection{Performance test} \end{asy} \end{minipage} -\begin{minipage}[h]{0.49\linewidth} +\begin{minipage}[h]{0.4\linewidth} \begin{asy} real linked = 1187; real shared = 3527; @@ -1454,7 +1454,7 @@ \subsection{Performance test} \end{minipage} % \begin{figure}[h!] -% \begin{minipage}[h]{0.49\linewidth}{1.0\textwidth} +% \begin{minipage}[h]{0.4\linewidth}{1.0\textwidth} % \centering % \includegraphics{alloc} % \label{fig:sub:subfigure1} From f504ca84dd0826f27016974168677f80da23146e Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 16:35:40 +0300 Subject: [PATCH 13/18] final upd --- smart-pointers.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 1201613..62f1fb8 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -940,7 +940,7 @@ \subsection{доп. функции} \subsection{Performance test} -Также уместно представить сравнение производительностей разных видов указателей: +Также уместно представить сравнение производительностей разных видов указателей. На приведенных графиках по оси OY отмечено среднее время соответствующей операции (в тактах процессора Intel Core i5-5257U @ 2.70GHz). Также отмечена дисперсия для каждого из измерений. \begin{minipage}[h]{0.4\linewidth} \centering From 5a6f5512c873aaf2165d13aeaad49aa39e294751 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 30 Dec 2017 17:28:41 +0300 Subject: [PATCH 14/18] after last review --- smart-pointers.tex | 85 +++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 62f1fb8..2fa8bb7 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -24,7 +24,7 @@ \section{Умные указатели} {c++} container* create_container() { - container* c = new container(); + container* c = new container(); try { fill(*c); @@ -116,7 +116,7 @@ \subsection{\mintinline{c++}{std::unique_ptr}} } \end{minted} -\mintinline{c++}{reset(p)} - заменяет ptr, хранящийся внутри, на p, и делает \mintinline{c++}{delete} старому ptr. +\mintinline{c++}{reset(p)} --- заменяет ptr, хранящийся внутри, на p, и делает \mintinline{c++}{delete} старому ptr. \begin{minted}[ linenos, frame=lines, @@ -166,7 +166,7 @@ \subsection{Владение} В некоторых случаях объект может иметь несколько владельцев. Это называется разделяемым владением и работает следующим образом: пока существует хотя бы один владелец объект продолжает жить, когда пропадает последний владелец --- объект удаляется. Для умных указателей существует два способа реализации разделяемого владения: подсчет ссылок и провязка всех владельцев в двусвязный список. Оба подхода имеют свои преимущества и недостатки. Подсчет ссылок применяется во включенном в стандартную библиотеку указателе \mintinline{c++}{std::shared_ptr}. Указатель использующий провязку владельцев в двусвязный список в стандартной библиотеке отсутствует, но часто называется \mintinline{c++}{linked_ptr}. \subsection{\mintinline{c++}{std::shared_ptr}} -\mintinline{c++}{std::shared_ptr} - это умный указатель, с разделяемым владением объектов через его указатель. Несколько указателей могут владеть одним объектом. Объект будет уничтожен, когда последний \mintinline{c++}{shared_ptr} будет уничтожен или сброшен. +\mintinline{c++}{std::shared_ptr} --- это умный указатель, с разделяемым владением объектов через его указатель. Несколько указателей могут владеть одним объектом. Объект будет уничтожен, когда последний \mintinline{c++}{shared_ptr} будет уничтожен или сброшен. \textcolor{red}{NB}) \mintinline{c++}{shared_ptr} может не указывать ни на какой объект. @@ -251,7 +251,7 @@ \subsubsection{Заметки по использованию.} \end{enumerate} \subsubsection{Weak\_ptr} -\mintinline{c++}{std::weak_ptr} - умный указатель, который моделирует временное владение объектом. +\mintinline{c++}{std::weak_ptr} --- умный указатель, который моделирует временное владение объектом. Ситуация: мы хотим хешировать картинки, которые хранять по \mintinline{c++}{std::shared_ptr}. То есть нам нужен мэп: name\_image $\to$ \mintinline{c++}{std::shared_ptr}. Но есть проблема, что если мы будем хранить в мэпе \mintinline{c++}{std::shared_ptr}, то они будут считать владельцами этих картинок, и они никогда не будут удаляться (если только их не удалить руками). В этой ситуации может помочь \mintinline{c++}{std::weak_ptr}. @@ -260,7 +260,7 @@ \subsubsection{Weak\_ptr} Дело в том, что weak\_ptr содержит "слабую"\ ссылку на объект, управляемый указателем \mintinline{c++}{std::shared_ptr}. Для этого и нужен счетчик "слабых" сслылок в блоке управления \mintinline{c++}{std::shared_ptr}. Теперь блок управления считает еще и слабые сслыки и когда удаляется управляемый объект, блок управления остается жить, пока на него ссылается хотя бы один weak\_ptr, чтобы сообщать им о существовании объекта. Например с помощью метода \mintinline[breaklines]{c++}{std::weak_ptr::expired() // проверяет удален ли объект.} -То есть все, что должен хранить внутри себя weak\_ptr - это указатель блок управления. +То есть все, что должен хранить внутри себя weak\_ptr --- это указатель блок управления. Теперь вернемся к нашей задаче: мы сделаем мэп: name\_image $\to$ weak\_ptr. @@ -283,7 +283,7 @@ \subsubsection{Weak\_ptr} \end{minted} -\textcolor{red}{NB}) Преобразования между \mintinline{c++}{std::shared_ptr} и weak\_ptr - это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. +\textcolor{red}{NB}) Преобразования между \mintinline{c++}{std::shared_ptr} и weak\_ptr --- это нормально. Только с его помощью можно обратиться к объекту weak\_ptr, не определяя новых указателей. \textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости \mintinline{c++}{std::shared_ptr}. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. @@ -300,9 +300,11 @@ \subsection{Сast\_pointer} bar = make_shared(); foo = dynamic_pointer_cast(bar); -// иначе пришлось бы писать так: -foo = shared_ptr(dynamic_cast(bar.get())); - +/* +иначе пришлось бы писать так: + foo = shared_ptr(dynamic_cast(bar.get())); +но этот код - некорректный, т.к. он приводит к сплиту указателя, хранящегося в bar. После этого на этом объекте гарантировано будет вызван деструктор два раза. +*/ \end{minted} \subsection{\mintinline{c++}{linked_ptr}} @@ -327,7 +329,7 @@ \subsection{\mintinline{c++}{linked_ptr}} \end{minted} Члены prev и next помечены как \mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2. -Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object : +Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object: \begin{minipage}[h]{0.4\linewidth} \centering @@ -755,25 +757,25 @@ \subsection{\mintinline{c++}{linked_ptr}} \subsubsection{сравнение \mintinline{c++}{linked_ptr} и \mintinline{c++}{shared_ptr}} \begin{enumerate} -\item \mintinline{c++}{std::shared_ptr} при передаче в конструктор указателя на объект аллоцирует еще 1 вспомогательный объект на хипе, а \mintinline{c++}{linked_ptr} - ничего. -\item \mintinline{c++}{linked_ptr} имеет больший размер, чем \mintinline{c++}{shared_ptr} --- три указателя, по сравненею с двумя у \mintinline{c++}{shared_ptr}. -\item \mintinline{c++}{std::shared_ptr} в стандартной библиотеке является thread-safe. Он использует атомарные операции инкремента и декремента для счетчика ссылок. Thread-safe \mintinline{c++}{linked_ptr} не может быть так просто реализован и требует использования уже thread-safe структур данных. -\item \mintinline{c++}{linked_ptr} также имеет внутри больше присваиваний и сравнений на каждую операцию копирования и присваивания, в то время как \mintinline{c++}{std::shared_ptr} должен только инкрементировать счетчик. +\item При инициализации \mintinline{c++}{std::shared_ptr} сырым указателем, \mintinline{c++}{std::shared_ptr} аллоцирует дополнительный объект в куче для счетчика ссылок. \mintinline{c++}{linked_ptr} не аллоцирует дополнительной памяти. +\item \mintinline{c++}{linked_ptr} имеет больший размер, чем \mintinline{c++}{shared_ptr} --- три указателя, вместо двух у \mintinline{c++}{shared_ptr}. +\item \mintinline{c++}{std::shared_ptr} в стандартной библиотеке является thread-safe. Он использует атомарные операции инкремента и декремента для счетчика ссылок. Thread-safe \mintinline{c++}{linked_ptr} не может быть так просто реализован. +\item \mintinline{c++}{linked_ptr} также имеет внутри больше присваиваний и сравнений на каждую операцию копирования и присваивания, в то время как \mintinline{c++}{std::shared_ptr} лишь инкрементирует счетчик. \end{enumerate} \subsection{\mintinline{c++}{intrusive_ptr}} -\mintinline{c++}{intrusive_ptr} - также умный указатель с разделяемым владением, реализованный в библиотеке Boost. Так же как и \mintinline{c++}{std::shared_ptr}, он использует подсчет ссылок, но в отличии от \mintinline{c++}{std::shared_ptr} в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. +\mintinline{c++}{intrusive_ptr} --- умный указатель с разделяемым владением, реализованный в библиотеке Boost. Так же как и \mintinline{c++}{std::shared_ptr}, он использует подсчет ссылок, но в отличии от \mintinline{c++}{std::shared_ptr} в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. -Для использования \mintinline{c++}{intrusive_ptr} требуется реализовать функции: +Для использования \mintinline{c++}{intrusive_ptr} с пользовательским типом \mintinline{c++}{mytype} требуется реализовать функции: \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -int intrusive_ptr_add_ref(T* p); -int intrusive_ptr_release(T* p); +int intrusive_ptr_add_ref(mytype* p); +int intrusive_ptr_release(mytype* p); \end{minted} Пример использования: @@ -820,25 +822,25 @@ \subsection{make\_shared / make\_unique} \end{minted} Исторически введение данного метода должно было решить проблему производительности \mintinline{c++}{std::shared_ptr}, аллоцируя внутри только один объект, содержащий одновременно и счетчик ссылок и объект типа T. Причем при вызове \mintinline{c++}{std::make_shared(args...)} на новом объекте типа T сразу вызовется конструктор с переданными аргументами. Таким образом такой конструктор-фабрика экономит аллокации памяти (вместо двух аллокаций: \mintinline{c++}{new T(args...)} и \mintinline{c++}{new counter()} происходит одна аллокация данных для пары (\mintinline{c++}{object}, \mintinline{c++}{counter}). -Также введение такой функции в стандарт позволило решить проблему с гарантиями безопасности. Представим следующую ситуацию : +Также введение такой функции в стандарт позволило решить проблему с гарантиями безопасности. Представим следующую ситуацию: \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -f(std::shared_ptr(new Class(“class”)), g()); +f(std::shared_ptr(new Class("class")), g()); \end{minted} -При этом \mintinline{c++}{g()} бросает исключение. Т.к. по стандарту нет гарантии порядка вычисления аргументов \mintinline{c++}{f}, то может произойти следующее : сначала создадим \mintinline{c++}{new Class()}, выделим память, потом - вызовем \mintinline{c++}{g()}, которое бросит исключение, после чего конструктор \mintinline{c++}{shared_ptr} уже не вызовется, т.к. бросился exception. А т.к. мы нигде не пишем слово \mintinline{c++}{delete}, то произойдет memory leak. +При этом \mintinline{c++}{g()} бросает исключение. так как по стандарту нет гарантии порядка вычисления аргументов \mintinline{c++}{f}, то может произойти следующее: сначала создадим \mintinline{c++}{new Class()}, выделим память, потом --- вызовем \mintinline{c++}{g()}, которое бросит исключение, после чего конструктор \mintinline{c++}{shared_ptr} уже не вызовется, так как бросился exception. А так как мы нигде не пишем слово \mintinline{c++}{delete}, то произойдет memory leak. -Теперь рассмотрим случай с \mintinline{c++}{make_shared} : +Теперь рассмотрим случай с \mintinline{c++}{make_shared}: \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -f(std::make_shared(“class”), g()); +f(std::make_shared("class"), g()); \end{minted} Если сначала вызовется \mintinline{c++}{g()}, то до конструктора \mintinline{c++}{Class} дело вообще не дойдет и упомянутой проблемы не возникнет. С другой стороны, если сначала вызовется \mintinline{c++}{make_shared}, то объект будет уже обернут в smart pointer и после исключения в \mintinline{c++}{g()} он будет автоматически удален!!! @@ -847,7 +849,7 @@ \subsection{make\_shared / make\_unique} \subsection{Smart pointers pointing on this} -Особый случай, когда нельзя просто так взять и использовать умные указатели бездумно --- это указатели на this, которые возвращает метод класса. Рассмотрим код : +Особый случай, когда нельзя неосторожно использовать умные указатели --- это наличие указателей на this, которые возвращает метод класса. Рассмотрим код: \begin{minted}[ linenos, @@ -856,23 +858,23 @@ \subsection{Smart pointers pointing on this} {c++} class SomeClass { - int data = 5; - std::shared_ptr f() { - return std::shared_ptr(this); - } - void foo() {} + int data = 5; + std::shared_ptr f() { + return std::shared_ptr(this); + } + void foo() {} } SomeClass obj; if (true) { // some scope - auto ptr = ob.f(); + auto ptr = ob.f(); } // exit scope (*) obj.foo(); \end{minted} -В этом примере при выхода из скоупа (*) т.к. умный указатель на obj создан всего 1, он вызовет деструктор \mintinline{c++}{obj}. Но при этом после этого мы уже не сможем его использовать (\mintinline{c++}{obj.foo()} уже некорректно). Поэтому для таких целей в stdlib есть интерфейс \mintinline{c++}{std::enable_shared_from_this}, оторый позволяет держать сильную ссылку на себя внутри самого объекта класса, который от него унаследован. В нашем случае : +В этом примере при выхода из скоупа (*) так как умный указатель на obj создан всего один, он вызовет деструктор \mintinline{c++}{obj}. Но при этом после этого мы уже не сможем его использовать (\mintinline{c++}{obj.foo()} уже некорректно). Поэтому для таких целей в стандартной библиотеке есть интерфейс \mintinline{c++}{std::enable_shared_from_this}, который позволяет держать сильную ссылку на себя внутри самого объекта класса, который от него унаследован. В нашем случае: \begin{minted}[ @@ -882,11 +884,11 @@ \subsection{Smart pointers pointing on this} {c++} class SomeClass: std::enable_shared_from_this { - int data = 5; - std::shared_ptr f() { - return std::shared_ptr(this); - } - void foo() {} + int data = 5; + std::shared_ptr f() { + return std::shared_ptr(this); + } + void foo() {} } @@ -894,7 +896,7 @@ \subsection{Smart pointers pointing on this} if (true) { - auto ptr = ob.f(); + auto ptr = ob.f(); } obj.foo(); @@ -919,7 +921,7 @@ \subsection{smart pointers и наследование} shared_ptr bp3(new Derived); \end{minted} -Это корректный код. Но стоит отметить, что в этом случае т.к. указатель внутри будет хранится на \mintinline{c++}{Derived} объект, то деструктор будет вызван ровно его. Поэтому если он не объявлен как виртуальный, то это UB. +Это корректный код. Но стоит отметить, что в этом случае так как указатель внутри будет хранится на \mintinline{c++}{Derived} объект, то деструктор будет вызван ровно его. Поэтому если он не объявлен как виртуальный, то это UB. Для явного каста указателей в стандартной библиотеке есть специальные методы: \begin{minted}[ linenos, @@ -930,17 +932,17 @@ \subsection{smart pointers и наследование} derived_ptr = static_pointer_cast(base_ptr); \end{minted} -Этот стейтмент валиден т.и.т.т, когда \mintinline{c++}{static_cast(base_ptr.get())} - валидно, т.к. Ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Его основное преимущество - отсутствие необходимости писать \mintinline{c++}{.get()} вручную и тем самым необходимости работать с классическими голыми указателями. +Этот стейтмент валиден т.и.т.т, когда \mintinline{c++}{static_cast(base_ptr.get())} --- валидно, так как Ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Использование \mintinline{c++}{static_pointer_cast} помогает избавиться от типичной ошибки --- использования \mintinline{c++}{static_cast(base_ptr.get())} в явном виде. Это использование может привести к тому, что деструктор на управляемом объекте будет вызван дважды --- при вызове деструктора \mintinline{c++}{base_ptr} и при вызове деструктора \mintinline{c++}{derived_ptr}. \subsection{доп. функции} \begin{enumerate} -\item Умные указатели умеют каститься к \mintinline{c++}{bool} (\mintinline{c++}{true} т.и т.т., когда указываемый объект не \mintinline{c++}{nullptr}) +\item Умные указатели умеют каститься к \mintinline{c++}{bool} (\mintinline{c++}{true} тогда и только тогда, когда указываемый объект не \mintinline{c++}{nullptr}) \item Также для них определены операции сравнения \mintinline{c++}{==}, \mintinline{c++}{!=}, \mintinline{c++}{<}, которые сравнивают голые указатели внутри. \end{enumerate} \subsection{Performance test} -Также уместно представить сравнение производительностей разных видов указателей. На приведенных графиках по оси OY отмечено среднее время соответствующей операции (в тактах процессора Intel Core i5-5257U @ 2.70GHz). Также отмечена дисперсия для каждого из измерений. +Также уместно представить сравнение производительностей разных видов указателей. На графиках ниже по оси OY отмечено среднее время соответствующих операций над умными указателями. Это время приведено в тактах процессора (для измерений использовался Intel Core i5-5257U). Также отмечена дисперсия каждого из измерений. \begin{minipage}[h]{0.4\linewidth} \centering @@ -1462,4 +1464,3 @@ \subsection{Performance test} % \caption{\textsl{Figure text.}} % \label{fig:whole_figure} % \end{figure} - From f4941a9a580e243433ca1025daa7cc6841397853 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Mon, 1 Jan 2018 18:22:53 +0300 Subject: [PATCH 15/18] some updates --- smart-pointers.tex | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 2fa8bb7..2920dda 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -288,8 +288,21 @@ \subsubsection{Weak\_ptr} \textcolor{red}{NB}) С помощью weak\_ptr можно решать циклические зависимости \mintinline{c++}{std::shared_ptr}. Суть проблемы в том, что объекты не могут удалиться, так как ссылаются друг на друга. +\subsection{split умных указателей} + +Расмотрим следующий код: + +% TODO +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +A p = new A(); +shared_ptr p1(p); +shared_ptr p2(p); +\end{minted} + +Этот код --- некорректный, т.к. оба умных указателя указывают на один и тот же объект p, но при вызове конструктора + \subsection{Сast\_pointer} -Что выполнять безоспасное приведение умных указателей можно искользовать слудующий синтаксис: +Что выполнять безопасное приведение умных указателей можно искользовать следующий синтаксис: \begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} struct A {/*...*/}; struct B: A {/*...*/}; @@ -300,11 +313,6 @@ \subsection{Сast\_pointer} bar = make_shared(); foo = dynamic_pointer_cast(bar); -/* -иначе пришлось бы писать так: - foo = shared_ptr(dynamic_cast(bar.get())); -но этот код - некорректный, т.к. он приводит к сплиту указателя, хранящегося в bar. После этого на этом объекте гарантировано будет вызван деструктор два раза. -*/ \end{minted} \subsection{\mintinline{c++}{linked_ptr}} @@ -767,17 +775,19 @@ \subsection{\mintinline{c++}{intrusive_ptr}} \mintinline{c++}{intrusive_ptr} --- умный указатель с разделяемым владением, реализованный в библиотеке Boost. Так же как и \mintinline{c++}{std::shared_ptr}, он использует подсчет ссылок, но в отличии от \mintinline{c++}{std::shared_ptr} в \mintinline{c++}{boost::intrusive_ptr} счетчик ссылок хранится в самом объекте класса T, на который он ссылается. -Для использования \mintinline{c++}{intrusive_ptr} с пользовательским типом \mintinline{c++}{mytype} требуется реализовать функции: +Для использования \mintinline{c++}{intrusive_ptr} с пользовательским типом \mintinline{c++}{mytype} требуется реализовать две функции: \begin{minted}[ linenos, frame=lines, framesep=2mm] {c++} -int intrusive_ptr_add_ref(mytype* p); -int intrusive_ptr_release(mytype* p); +void intrusive_ptr_add_ref(mytype* p); +void intrusive_ptr_release(mytype* p); \end{minted} +Функция intrusive_ptr_add_ref вызывается, когда требуется увеличить счетчик ссылок. Функция intrusive_ptr_release вызывается тогда, когда требуется его уменьшить и если он достигает нуля, то требуется самостоятельно удалить объект. + Пример использования: \begin{minted}[ @@ -874,7 +884,7 @@ \subsection{Smart pointers pointing on this} \end{minted} -В этом примере при выхода из скоупа (*) так как умный указатель на obj создан всего один, он вызовет деструктор \mintinline{c++}{obj}. Но при этом после этого мы уже не сможем его использовать (\mintinline{c++}{obj.foo()} уже некорректно). Поэтому для таких целей в стандартной библиотеке есть интерфейс \mintinline{c++}{std::enable_shared_from_this}, который позволяет держать сильную ссылку на себя внутри самого объекта класса, который от него унаследован. В нашем случае: +В этом примере при выхода из области видимости (*) так как умный указатель на obj создан всего один, он вызовет деструктор \mintinline{c++}{obj}. Но при этом после этого мы уже не сможем его использовать (\mintinline{c++}{obj.foo()} уже некорректно). Поэтому для таких целей в стандартной библиотеке есть интерфейс \mintinline{c++}{std::enable_shared_from_this}, который позволяет держать сильную ссылку на себя внутри самого объекта класса, который от него унаследован. В нашем случае: \begin{minted}[ @@ -902,7 +912,7 @@ \subsection{Smart pointers pointing on this} \end{minted} -Такой код будет уже корректный. +Такой код будет уже корректный. Таким образом использование интерфейса \mintinline{c++}{std::enable_shared_from_this} позволяет избежать проблемы сплита указателей. \subsection{smart pointers и наследование} @@ -932,7 +942,7 @@ \subsection{smart pointers и наследование} derived_ptr = static_pointer_cast(base_ptr); \end{minted} -Этот стейтмент валиден т.и.т.т, когда \mintinline{c++}{static_cast(base_ptr.get())} --- валидно, так как Ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Использование \mintinline{c++}{static_pointer_cast} помогает избавиться от типичной ошибки --- использования \mintinline{c++}{static_cast(base_ptr.get())} в явном виде. Это использование может привести к тому, что деструктор на управляемом объекте будет вызван дважды --- при вызове деструктора \mintinline{c++}{base_ptr} и при вызове деструктора \mintinline{c++}{derived_ptr}. +Этот стейтмент валиден тогда и только тогда, когда \mintinline{c++}{static_cast(base_ptr.get())} --- валидно, так как ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Использование \mintinline{c++}{static_pointer_cast} помогает избавиться от типичной ошибки --- использования \mintinline{c++}{static_cast(base_ptr.get())} в явном виде. Это использование может привести к тому, что деструктор на управляемом объекте будет вызван дважды --- при вызове деструктора \mintinline{c++}{base_ptr} и при вызове деструктора \mintinline{c++}{derived_ptr}. \subsection{доп. функции} \begin{enumerate} From 66067fcaa64a8b9c81031239db4ac50e7525b7d9 Mon Sep 17 00:00:00 2001 From: Ololoshechkin Date: Tue, 2 Jan 2018 21:15:48 +0300 Subject: [PATCH 16/18] final --- smart-pointers.tex | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 2920dda..475a772 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -291,30 +291,42 @@ \subsubsection{Weak\_ptr} \subsection{split умных указателей} Расмотрим следующий код: - -% TODO \begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -A p = new A(); -shared_ptr p1(p); -shared_ptr p2(p); +SomeClass p = new SomeClass(); +shared_ptr p1(p); +shared_ptr p2(p); \end{minted} - -Этот код --- некорректный, т.к. оба умных указателя указывают на один и тот же объект p, но при вызове конструктора +Этот код --- некорректный, т.к. оба умных указателя указывают на один и тот же объект p, но при вызове конструктора от p каждый из умных указателей создаст свой счетчик ссылок, проинициализировав его значением 1. При выходе из области видимости каждый из созданных умных указателей попытается удалить объект p, продекрементировав свой счетчик ссылок. В обоих случаях он станет равным нулю, после чего будет дважды вызван деструктор на объекте p. Описанная ситуация называется разделением (split) умных указателей. \subsection{Сast\_pointer} -Что выполнять безопасное приведение умных указателей можно искользовать следующий синтаксис: +Рассмотрим пример кода: +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +struct Base {/*...*/}; +struct Derived: Base {/*...*/}; + +shared_ptr foo; +shared_ptr bar; +/*...*/ +bar = make_shared(); + +foo = shared_ptr(dynamic_cast(bar.get())); +\end{minted} +В приведенном примере кода происходит создание умного указателя на базовый класс от уже имеющегося умного указателя на производный класс. Но этот код содержит ту же ошибку, что была рассмотрена в предыдущем параграфе --- указатели foo и bar указывают на один и тот же объект. +Чтобы выполнять безопасное приведение умных указателей можно искользовать следующий синтаксис: \begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} -struct A {/*...*/}; -struct B: A {/*...*/}; +struct Base {/*...*/}; +struct Derived: Base {/*...*/}; -shared_ptr foo; -shared_ptr bar; +shared_ptr foo; +shared_ptr bar; /*...*/ -bar = make_shared(); +bar = make_shared(); -foo = dynamic_pointer_cast(bar); +foo = dynamic_pointer_cast(bar); \end{minted} +Функция \mintinline{c++}{dynamic_pointer_cast(ptr)} принимает умный указатель ptr на объект типа Base и возвращает другой умный указатель на тот же объект, но приведенный к типу Derived. Внутри себя \mintinline{c++}{dynamic_pointer_cast} использует \mintinline{c++}{dynamic_cast} для приведения типов, а также инкрементирует счетчики ссылок для умных указателей. Благодаря этому split'а умных указателей не происходит и деструктор на переданном в \mintinline{c++}{dynamic_pointer_cast} объекте будет вызван ровно один раз. + \subsection{\mintinline{c++}{linked_ptr}} \mintinline{c++}{linked_ptr} --- умный указатель с разделяемым владением, реализованный с помощью двусвязного списка. @@ -786,7 +798,7 @@ \subsection{\mintinline{c++}{intrusive_ptr}} void intrusive_ptr_release(mytype* p); \end{minted} -Функция intrusive_ptr_add_ref вызывается, когда требуется увеличить счетчик ссылок. Функция intrusive_ptr_release вызывается тогда, когда требуется его уменьшить и если он достигает нуля, то требуется самостоятельно удалить объект. +Функция \mintinline{c++}{intrusive_ptr_add_ref} вызывается, когда требуется увеличить счетчик ссылок. Функция \mintinline{c++}{intrusive_ptr_release} вызывается тогда, когда требуется его уменьшить и если он достигает нуля, то требуется самостоятельно удалить объект. Пример использования: @@ -942,7 +954,7 @@ \subsection{smart pointers и наследование} derived_ptr = static_pointer_cast(base_ptr); \end{minted} -Этот стейтмент валиден тогда и только тогда, когда \mintinline{c++}{static_cast(base_ptr.get())} --- валидно, так как ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Использование \mintinline{c++}{static_pointer_cast} помогает избавиться от типичной ошибки --- использования \mintinline{c++}{static_cast(base_ptr.get())} в явном виде. Это использование может привести к тому, что деструктор на управляемом объекте будет вызван дважды --- при вызове деструктора \mintinline{c++}{base_ptr} и при вызове деструктора \mintinline{c++}{derived_ptr}. +Этот стейтмент валиден тогда и только тогда, когда \mintinline{c++}{static_cast(base_ptr.get())} --- валидно, так как ровно на его основе метод \mintinline{c++}{static_pointer_cast} реализован. Использование \mintinline{c++}{static_pointer_cast} помогает избавиться от типичной ошибки --- использования \mintinline{c++}{static_cast(base_ptr.get())} в явном виде, приводящего к split'у умных указателей. \subsection{доп. функции} \begin{enumerate} From f28dc0cb2ff212be3aa4340e654a6bcea2a5c75a Mon Sep 17 00:00:00 2001 From: Ololoshechkin Date: Tue, 2 Jan 2018 21:32:44 +0300 Subject: [PATCH 17/18] final --- smart-pointers.tex | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 475a772..3d7a98c 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -311,8 +311,7 @@ \subsection{Сast\_pointer} foo = shared_ptr(dynamic_cast(bar.get())); \end{minted} -В приведенном примере кода происходит создание умного указателя на базовый класс от уже имеющегося умного указателя на производный класс. Но этот код содержит ту же ошибку, что была рассмотрена в предыдущем параграфе --- указатели foo и bar указывают на один и тот же объект. -Чтобы выполнять безопасное приведение умных указателей можно искользовать следующий синтаксис: +В приведенном примере кода происходит создание умного указателя на базовый класс от уже имеющегося умного указателя на производный класс. Но этот код содержит ту же ошибку, что была рассмотрена в предыдущем параграфе --- split умных указателей: указатели \mintinline{c++}{foo} и \mintinline{c++}{bar} указывают на один и тот же объект, а их счетчики ссылок не были продекрементированы в момент вызова конструктора \mintinline{c++}{foo}. \\ Чтобы выполнять безопасное приведение умных указателей можно искользовать следующий синтаксис: \begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} struct Base {/*...*/}; struct Derived: Base {/*...*/}; @@ -329,8 +328,7 @@ \subsection{Сast\_pointer} \subsection{\mintinline{c++}{linked_ptr}} -\mintinline{c++}{linked_ptr} --- умный указатель с разделяемым владением, реализованный с помощью двусвязного списка. - +\mintinline{c++}{linked_ptr} --- умный указатель с разделяемым владением, реализованный с помощью двусвязного списка.\\ \mintinline{c++}{linked_ptr} хранит в себе указатель на объект и два указателя на соседние \mintinline{c++}{linked_ptr}'ы в двусвязном списке. Для каждого двусвязного списка образованного из \mintinline{c++}{linked_ptr}'ов верно, что все указатели в нём владеют одним общим объектом. \begin{minted}[ @@ -347,9 +345,8 @@ \subsection{\mintinline{c++}{linked_ptr}} mutable linked_ptr* next; }; \end{minted} -Члены prev и next помечены как \mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2. - -Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object: +Члены prev и next помечены как \mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2.\\ +Так, например, может выглядеть фрагмент двусвязного списка, образованного \mintinline{c++}{linked_ptr}'ами ptr1, ptr2, ptr3, указывающими на общий объект object:\\ \begin{minipage}[h]{0.4\linewidth} \centering @@ -518,7 +515,7 @@ \subsection{\mintinline{c++}{linked_ptr}} \label{fig:prob1_6_2} \end{minipage} -При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом: +При копировании \mintinline{c++}{linked_ptr ptr4 = ptr2;} новый ptr4 вставляется в тот же список, где был исходный ptr2. После выполнения этой операции двусвязный список указателей будет выглядеть следующим образом:\\ \begin{minipage}[h]{0.4\linewidth} \centering From 661a7d8512ff8561b27898ca50389e3e697b3dd5 Mon Sep 17 00:00:00 2001 From: Vadim Brilyantov Date: Sat, 6 Jan 2018 01:20:52 +0300 Subject: [PATCH 18/18] small upds --- smart-pointers.tex | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/smart-pointers.tex b/smart-pointers.tex index 3d7a98c..082c5bc 100644 --- a/smart-pointers.tex +++ b/smart-pointers.tex @@ -341,8 +341,8 @@ \subsection{\mintinline{c++}{linked_ptr}} ... private: T* ptr; - mutable linked_ptr* prev; - mutable linked_ptr* next; + mutable linked_ptr const* prev; + mutable linked_ptr const* next; }; \end{minted} Члены prev и next помечены как \mintinline{c++}{mutable}, потому что в случае если мы имеем константный \mintinline{c++}{const linked_ptr& p1}, который передается в функцию, и неконстантный \mintinline{c++}{linked_ptr& p2}, который является его соседом в двусвязном списке, то при изменении p1 придется поменять prev и next у p2.\\ @@ -728,11 +728,13 @@ \subsection{\mintinline{c++}{linked_ptr}} ~linked_ptr() noexcept { - if (!ptr) return; - if (prev == this) - delete ptr; - if (prev) prev->next = next; - if (next) next->prev = prev; + if (!payload) return; + if (prev == this) { + delete payload; + } else { + if (prev) prev->next = next; + if (next) next->prev = prev; + } } \end{minted}