С Новым годом! Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.77/13: Рейтинг темы: голосов - 13, средняя оценка - 4.77
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11

Shared_ptr и парадигма CoW

20.04.2020, 22:54. Показов 3156. Ответов 42
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Задача:
Написать простенький аналог shared_ptr применяя парадигму Copy on Write.

Проблема:
Не могу понять в какой момент дергать функцию создания данных и как правильно это сделать.
Как реализовать функции void ensureInitialized() const и void ensureUnique().

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include "datahandler.h"
 
using namespace std;
 
DataHandler::DataHandler(const DataHandler &h)
{
    if (data == h.data)
        return;
    if (h.data == nullptr)
        return;
    if (h.refCounter == nullptr)
        return;
 
    (*h.refCounter)--;
 
    if((*h.refCounter) == 0)
    {
        delete h.refCounter;
        delete h.data;
    }
 
    data = h.data;
    refCounter = h.refCounter;
    (*refCounter)++;
}
 
DataHandler::~DataHandler()
{
    if (refCounter == nullptr)
        return;
 
    (refCounter)--;
 
    if((*refCounter) == 0)
    {
        delete refCounter;
        delete data;
    }
    refCounter = 0;
    data = 0;
}
 
Data* DataHandler::operator ->()
{
    return data;
}
 
const Data* DataHandler::operator ->() const
{
    ensureInitialized();
    return data;
}
 
void DataHandler::ensureInitialized() const
{
    if (data == nullptr)
    {
 
        Data temp;
        *data = temp;
    }
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#ifndef DATAHANDLER_H
#define DATAHANDLER_H
 
#include <iostream>
 
using namespace std;
 
// Отвечает за хранение, создание и удаление наших ресурсов.
// Указатель на выделенный буфер, размер строки и тд. - все тут.
class Data
{
public:
    Data() {cout << "Data created\n";} // выделяем память
    Data(const Data&) {cout << "Data copied\n";} // делаем полную копию
    ~Data() {cout << "Data deleted\n";} // подчищаем
 
    void read() const {cout << "Data read() \n";}
    void write() {cout << "Data write() \n";}
};
 
// Отвечает за разделение ресурсов и ленивую инициализацию.
class DataHandler
{
public:
    DataHandler() = default;
    // Если у h есть данные, привязываемся к ним,
    // не забываем про счетчик ссылок. Можно еще по аналогии оператор "="
    // перегрузить.
    DataHandler(const DataHandler &h);
    // Если держим данные, уменьшаем счетчик ссылок, если больше никто на
    // эти данные не ссылается - удаляем.
    ~DataHandler();
 
    // Убеждаемся, что данные есть и спокойно возвращаем.
    const Data *operator ->() const;
    // Убеждаемся, что данные уникальны и спокойно возвращаем.
    Data *operator ->();
 
private:
    mutable int *refCounter; // указатель на счетчик ссылок
    mutable Data *data = nullptr;
 
    // Проверяем, держим ли мы данные, если нет - создаем.
    void ensureInitialized() const;
    // Проверяем, что на эти данные (если они вообще есть) никто кроме
    // нас не ссылается, если нет - создаем себе копию.
    void ensureUnique();
};
 
#endif
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
 
#include "datahandler.h"
 
using namespace std;
 
// Пример использования нашего умного DataHandler`a в другом
// классе. У вас его будет использовать MyString.
class DataUser 
{
public:
    void read() const {dataHandler->read();}
    void write() {dataHandler->write();}
 
private:
    DataHandler dataHandler;
};
 
int main()
{
    // Ничего не происходит, у нас же ленивая инициализация.
    DataUser du1; 
    
    // Создали данные и обратились к ним на чтение.
    du1.read(); 
    
    // Привязываемся к созданным данным.
    DataUser du2(du1);
    
    // Новые данные не создаются, т. к. обращение на чтение.
    du2.read();
 
    // Идет обращение на запись, поэтому сначала создаем себе
    // копию.
    du2.write();
}
 
// Вывод:
// Data created
// Data read() 
// Data read() 
// Data copied
// Data write() 
// Data deleted
// Data deleted
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.04.2020, 22:54
Ответы с готовыми решениями:

Сложный калькулятор на обратной польской (импиративная парадигма)
Доброго времени суток! Задание, написать сложный калькулятор не используя ООП Сделал на основе своего &quot;стека&quot;, все методы...

Парадигма программирования php
Здравствуйте. Есть разработанный сайт на PHP. Пользовательский интерфейс+админка. Сам сайт - справочно-информационная система....

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

42
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
20.04.2020, 23:08
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Как реализовать функции void ensureInitialized() const и void ensureUnique().
Ты для начала конструктор/оператор копирования реализуй правильно. Там объект, который передаётся параметром, из которого ты копируешь, не должен изменяться.
1
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
20.04.2020, 23:48  [ТС]
Переделал конструктор копирования.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include "datahandler.h"
 
using namespace std;
 
DataHandler::DataHandler(const DataHandler &h)
{
    if(data == h.data)
        return;
    if(refCounter == 0)
        return;
 
    --(*refCounter);
 
    if( (*refCounter)== 0 )
    {
        delete data;
        delete refCounter;
    }
 
    if(h.data == 0)
        return;
 
    data = h.data;
    refCounter = h.refCounter;
    (*refCounter)++;
}
 
DataHandler::~DataHandler()
{
    if (refCounter == nullptr)
        return;
 
    (refCounter)--;
 
    if((*refCounter) == 0)
    {
        delete refCounter;
        delete data;
    }
    refCounter = 0;
    data = 0;
}
 
Data* DataHandler::operator ->()
{
    return data;
}
 
const Data* DataHandler::operator ->() const
{
    ensureInitialized();
    return data;
}
 
void DataHandler::ensureInitialized() const
{
    if (data == nullptr)
    {
 
        Data temp;
        *data = temp;
    }
}
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
20.04.2020, 23:53
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Переделал конструктор копирования.
C++
1
2
3
4
5
6
7
8
DataHandler::DataHandler(const DataHandler &h)
{
    data = h.data;
    refCounter = h.refCounter;
 
if (refCounter  && *refCounter != 0)
    (*refCounter)++;
}
Добавлено через 3 минуты
Теперь нужно определиться, как именно ты собираешься изменять объект. Неконстантный оператор -> здесь не подходит
1
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 00:09  [ТС]
Объект, который мы передаем в параметре конструктора копирования является константным, получается мы его не меняем. Усвоил, за это большое спасибо!)

Если мы хотим прочитать данные, то вызывается константный оператор ->,
Если данные будут изменены, то вызывается неконстантный оператор ->.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:15
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Если данные будут изменены, то вызывается неконстантный оператор ->.
Он всегда будет вызываться. Константный вызывается только когда объект или ссылка явно объявлены константными.
Я бы сделал метод modify(), который будет возвращать ссылку на объект, если refCounter == 1, иначе создавать копию и возвращать ссылку.
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:17
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Если данные будут изменены, то вызывается неконстантный оператор ->.
Ну вот в неконстантном для неуникального объекта надо создавать копию.
1
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:27
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Ну вот в неконстантном для неуникального объекта надо создавать копию.
Константный -> будет вызываться крайне редко, скорее всего - вообще никогда. Получится, что на каждый чих ты будешь создавать копию. В этом случае накладных расходов будет больше, чем пользы.

Ailuropoda, попробуй - сделай пример и посмотри, когда у тебя будет вызываться константная стрелочка, а когда неконстантная.
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:33
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Константный -> будет вызываться крайне редко, скорее всего - вообще никогда. Получится, что на каждый чих ты будешь создавать копию. В этом случае накладных расходов будет больше, чем пользы.
А в другом месте ее не создашь. Потому что если вернуть неконстантный объект уже никак не перехватишь сами изменения. Вернуть же прокси не получится - обертки всех методов универсально не сделаешь, только операторов.
Поэтому CoW имеет смысл только для строк и т.д. - т.е. массивов примитивных типов ну или типов с которыми можно общаться только посредством операторов.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 00:38  [ТС]
Цитата Сообщение от Ailuropoda Посмотреть сообщение
// Создали данные и обратились к ним на чтение.
    du1.read();
На этом моменте происходит следующее:
1) Дергается функция void DataUser::read() const
2) Внутри нее дергается const Data* DataHandler::operator ->() const

Получается, что вызывается константная стрелочка. Или я окончательно запутался...
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:38
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
А в другом месте ее не создашь. Потому что если вернуть неконстантный объект уже никак не проверишь что там меняют.
Поэтому здесь стрелочка должна всегда возвращать константную ссылку, а для изменения нужно использовать неконстантный метод, который просто так, случайно, не вызовешь.
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:40
oleg-m1973, Ну тогда неконстантного -> так же как и неконстантного * вообще не должно быть. Как на меня - лучше четко контролировать где константный доступ где нет,, чем так уродоваться. Но в общем в таких конструкциях смысла около нуля - смысла делать его CoW чтобы вызывать специальный метод нету, это делается для того чтобы именно этим вопросом - помнить где его скопировать надо не заморачиваться. А так проще сделать объекту метод создания копии и не парится.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:43
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
oleg-m1973, как на меня - лучше четко контролировать где константный доступ где нет. Но в общем в таких конструкциях смысла около нуля.
Как ты это собираешься контролировать для shared_ptr при помощи операторов доступа к содержимому? С учётом того, что shared_ptr обычно передаётся по-значению.
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:46
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Как ты это собираешься контролировать для shared_ptr при помощи операторов доступа к содержимому?
Лучшее что можно сделать с shared_ptr и weak_ptr - вообще выбросить. Для управления активными сущностями это не предназначено, а для пассивных буферов - счетчик должен жить в самом буфере, и строить из них иерархии тоже категорически противопоказано.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 00:47  [ТС]
Какая у вас дискуссия по этому поводу...
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:48
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Лучшее что можно сделать с shared_ptr и weak_ptr - вообще выбросить.
Да? А взамен что?
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:52
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Да? А взамен что?
Ну естественно что двунаправленные смарты, а не этот костыль-эмулятор GC, предназначенный для облегчения перехода с явы.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 00:55
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Ну естественно что двунаправленные смарты,
Что такое "двунаправленные смарты" (я реально впервые слышу)?
0
 Аватар для Fulcrum_013
2083 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 00:59
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
то такое "двунаправленные смарты" (я реально впервые слышу)?
Объект запоминает всех кто на него ссылается. О факте смерти уведомляет - как результат и из контейнеров ссылки на убиенных автоматом вычищается и просто указатели обнуляются. Ну вы как автоматы для композиции и агрегации соответсвующие правилам ООП никогда не реализовывли, или итераторам/контейнерам std под капот не смотрели. Это кстати то для чего автоматика управления жизненным циклом предназначена - эффективная реализация всегда задачезависема. А смарты stl - так, затычка для самых бедных, которая ни автоматику взаимосвязей по правилам ООП реализовать не умеет, ни самоудаление объектов не поддерживает.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 01:02
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Объект запоминает всех кто на него ссылается. О факте смерти уведомляет - как результат и из контейнеров ссылки на убиенных автоматом вычищается и просто указатели обнуляются. Ну вы как автоматы для композиции и агрегации соответсвующие правилам ООП никогда не реализовывли, или итераторам/контейнерам std под капот не смотрели.
Не, не вариант. Счётчик как-то проще и дешевле, на порядок.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
21.04.2020, 01:02
Помогаю со студенческими работами здесь

COW copy on write, класс MyString
Нужно реализовать COW, но никак все не получается main.cpp #include &lt;iostream&gt; #include &quot;mystring.h&quot; using...

Описать класс Cow, который наследуется от класcа Animal
Нужно написать класс Cow, который наследуется от класcа Animal. Класс Animal имеет метод getName (name можно передать в конструктор). Класс...

Написать класс Cow, который наследуется от класcа Animal
Попросили помочь найти решение, а я даже не знаю куда обратиться. Нужно написать класс Cow, который наследуется от класcа Animal....

Создаем объект cow, у которого есть метод message, который принимает аргументом text и возвращает его
Создаем объект cow, у которого есть метод message, который принимает аргументом text и возвращает его. Создаем 2 блока try catch: ...

Создать абстрактный базовый класс Animal и производные классы Rooster, Goose, Turkey, Duck, Goat, Ram, Cow, Horse
Здравствуйте, не могу допереть как сделать последний пункт задания.Вот само задание 1. Создать абстрактный базовый класс Animal с чистой...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru