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

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

20.04.2020, 22:54. Показов 3431. Ответов 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
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 01:08
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Счётчик как-то проще и дешевле, на порядок.
Счетчик годен только для пассивных буферов. Для активных сущностей обратные ссылки все равно нужны для взаимодействия - это же ООП.

Добавлено через 4 минуты
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Не, не вариант.
Ну да. Везде кроме пассивных буферов ака строки счетчик не вариант. Он не способен обеспечить необходимое поведение.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 01:08  [ТС]
Цитата Сообщение от Ailuropoda Посмотреть сообщение
// Новые данные не создаются, т. к. обращение на чтение.
    du2.read()
На этом моменте происходит следующее:
1) Дергается функция void DataUser::read() const
2) Внутри нее дергается const Data* DataHandler::operator ->() const
3) Внутри const Data* DataHandler::operator ->() const мы сначала дергаем void DataHandler::ensureInitialized() const, который в случае refCountrer == 0 создаст нам новый объект. То есть создать новый объект класса DataHandler, который дернет конструктор Data?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 01:12
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Счетчик годен только для пассивных буферов.
Да нет. Ты сообщаешь объекту, что владеешь им. Объект не обязан ничего знать о тебе. Никакой двунаправленности (я даже боюсь представить как эти "двунаправленные смарты" реализуются, ещё и многопоточные).

Добавлено через 2 минуты
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Внутри const Data* DataHandler::operator ->() const мы сначала дергаем void DataHandler::ensureInitialized() const, который в случае refCountrer == 0 создаст нам новый объект.
Не надо ничего создавать внутри константного оператора.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 01:14  [ТС]
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Поэтому CoW имеет смысл только для строк и т.д
Эту простенькую задачку нам дали, для того чтобы мы изучили CoW на вот таком небольшом примере, а после этого нужно будет применить СoW к строкам... Боюсь, что если я сейчас не могу разобраться, то к строкам можно даже не притрагиваться)

Поэтому и обратился сюда за помощью.
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 01:16
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Ты сообщаешь объекту, что владеешь им.
Только в ООП никакого владения не существует. Существует два вида взаимосвязей - агрегация - при ее разрывее объект на который ссылались выживает, и композиция - при ее разрыве он перманентно и безоговорочно уничтожается. При этом разрыв ссылок это не то что не единственная, это вообще очень редкая причина удаления. Вообще в общем случае критерии того что объект не может продолжать существование знает только он сам и больше никто.
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
ещё и многопоточные
Параллелить надо или в режиме разделения ресурсов или в другом месте. К примеру именно по этой причине std::string перевели с СoW на move семантику.

Добавлено через 32 секунды
Ailuropoda, применяй сразу к строкам. там это гораздо проще. там окромя крайне неэффективного варианта скопировать в неконстантном [] есть еще вариант вернуть из него прокси-обертку.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 01:22  [ТС]
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
применяй сразу к строкам. там это гораздо проще.
К сожалению для начала нужно сделать это... Потому что я пока так и не понял что к чему видимо.

Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
там окромя крайне неэффективного варианта скопировать в неконстантном [] есть еще вариант вернуть из него прокси-обертку.
Что такое прокси-обертка, пока не знаю
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
21.04.2020, 01:22
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Эту простенькую задачку нам дали, для того чтобы мы изучили CoW на вот таком небольшом примере, а после этого нужно будет применить СoW к строкам... Боюсь, что если я сейчас не могу разобраться, то к строкам можно даже не притрагиваться)
Со строк и стоило начинать. Обобщить copy-on-write на любые объекты довольно сложно.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 01:25  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Не надо ничего создавать внутри константного оператора.
А для чего тогда нужен метод void DataHandler::ensureInitialized() const ? Я думал как раз в том случае, когда нам нужно убедиться есть ли у нас данные, если же их нет, то этот метод создаст их.
Видимо я не понимаю "логику" данной задачи...
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 01:29
Цитата Сообщение от Ailuropoda Посмотреть сообщение
то такое прокси-обертка, пока не знаю
объект которы содержит в себе значение (для строки символ) с обернутыми всеми операторами и ссылкой на строку. Ну а уже при присваивании и т.д. этому прокси уже и создается копия если необходимо. т.е. оно перехватывает действие записи.

Добавлено через 3 минуты
Цитата Сообщение от Ailuropoda Посмотреть сообщение
К сожалению для начала нужно сделать это...
Ну самый простой способ - копировать в неконстантном ->. Но опять же - для активных сущностей CoW - это бессмысленно по определению, а у пассивных -> быть не должно.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 01:37  [ТС]
Цитата Сообщение от Ailuropoda Посмотреть сообщение
// Вывод:
// Data created
// Data read()
// Data read()
// Data copied
// Data write()
// Data deleted
// Data deleted
Такого конечного результата нужно добиться путем вызова всех этих функций. Как я понял, то в момент
Цитата Сообщение от Ailuropoda Посмотреть сообщение
// Создали данные и обратились к ним на чтение.
    du1.read();
мы как раз и должны первый раз создать объект(ленивая инициализация), получается внутри функции
C++
1
DataUser::void read() const
мы должны вызвать функцию
C++
1
void DataHandler::ensureInitialized() const
0
 Аватар для Fulcrum_013
2083 / 1576 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
21.04.2020, 01:46
Цитата Сообщение от Ailuropoda Посмотреть сообщение
мы как раз и должны первый раз создать объект(ленивая инициализация), получается внутри функции
Вообще CoW использую только для строк. Но как бы этот момент есть и там. Чтобы с ним не парится - заголовок буфера для пустой строки делаю статической константой и в дефаултном конструкторе просто инициализирую указатель ее адресом. НУ а дальше просто в операторе сравнения с nullptr сравниваю значение с этим адресом а не с nullptr. и никакой мороки с дефаултными объектами. Ну разве что перед удалением а не на константу ли мы часом ссылаемся проверять надо.
А вообще почему для чего то кроме строк это в общем то бессмысленно - в строках все изменение буфера на который сылаются строка идет через методы самой строки а не буфера на который она ссылается и это элементарно обрабатывается. У произвольного объекта без оповещений с его стороны это сделать не удастся.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
21.04.2020, 02:06
Лучший ответ Сообщение было отмечено Ailuropoda как решение

Решение

Цитата Сообщение от Ailuropoda Посмотреть сообщение
C++
1
(refCounter)--;
Ailuropoda, я правилно понял - вы не инкрементировали счётчик и передвинули указатель на счётчик?
Может
C++
1
--(*refCounter);
?
И общий вопрос. Вы уже писали умный указатель считающий ссылки? Если да, то остаётся понять, что выделение памяти для создания собственного объекта, должно происходить лишь при попытке изменения объекта. Это значит, что все читают одну книжку, но как только кто-то решает сделать пометку на полях, - идёт и покупает собственный экземпляр. С этого момента счётчик начинает работать и изменённый объект может стать новой разшаренной книжкой для какой-то группы клиентов. Вот и всё. Создавая новый объект нужно уйти красиво из прежней группы уменьшив счётчик если там ещё кто-то есть (>1), а если объект видит что он один остался то, можно вообще этому смарту не делать ни чего, - просто выполнять модифицирующую операцию и всё. А уж если в группе есть ещё по крайней мере один владелец (счётчик>1) - уменьшаем счетчик (я умер для этого объекта /император Нерон/) и делать данные выделяя память - записывая копию + модификация. И счетчик на старт (=1).
Советую поиграть в ролевые игры с книжками, коврижками и прочими бутербродами. Может помочь)
Что касается строк, то это имхо не важно. Вы же не в типе данных CoW будете доить. Вы смарте будете. Это значит, что любой тип данных будет жить в куче. Там и строки могут жить если вы захотите.
0
0 / 0 / 0
Регистрация: 20.04.2020
Сообщений: 11
21.04.2020, 02:38  [ТС]
Большое спасибо за столь яркий пример!)
По поводу умного указателя, а тем более со счётчиком... Это первый опыт написания данного класса, как и первое знакомство с CoW.
Просто с самого начала... Не могу понять в какой именно момент нам проверить есть ли у нас вообще "книга" и если ее нет, то необходимо создать и сделать инкремент счетчика. Когда вызывается метод read, то мы просто инкрементируем счётчик у datahandler. Когда вызывается метод write, мы создаём новый объект, и делаем инкремент у него, перед этим сделав дикремент счётчика у предыдущего объекта?

Запутался в этих трёх классах, так ещё и Datahandler используется в классе Data... Вообщем полная каша.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
21.04.2020, 03:43
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Большое спасибо за столь яркий пример!)
Не за что)
Цитата Сообщение от Ailuropoda Посмотреть сообщение
По поводу умного указателя, а тем более со счётчиком... Это первый опыт написания данного класса, как и первое знакомство с CoW.
А вот это грустно. Сначала нужно бы разные варианты смартиков попрактиковать. Там насколько я понимаю, операторы * и -> будут реагировать только на константность самого смарта. Поэтому, реализация такого указателя как CoW это не нормально в том смысле, что указателем он не будет. Оберткой будет. Потому как ему придётся положить между интерфейсом указателя и содержимым методы типа read/write.
Наподобие:
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>
using namespace std;
template<class T>
class CoWptr
{
    public:
      CoWptr()
      :ref_counter(new int(0))
      {}
 
        CoWptr(const T &rval)
        :ptr(new T(rval))
        ,ref_counter(new int(1))
        {}
 
 
            ~CoWptr()
               {
                   liveGroup();
               }
 
              const T & readData()
               {
                   return ((const CoWptr*)this)-> operator*() ;
               }
 
               T & writeData()
               {
                   return this-> operator*() ;
               }
 
            const T * readPtr()
               {
                   return ((const CoWptr*)this)-> operator->() ;
               }
 
               T * writePtr()
               {
                   return this-> operator->() ;
               }
 
        private:
               T& operator*(){ return *ptr ;}
               const T& operator*()const{cout<<" operator*()"; return *ptr ;}
               T* operator->(){return ptr ;}
               const T* operator->()const{cout<<" operator->()"; return ptr ;}
 
 
    void liveGroup()//это вызывать на методах записи - не реализовал т.к. цель - заставить монстрягу реагировать на запись и чтение вызовом разных функций. Что вобщем-то оказалось излишне
               {
                   if(*ref_counter<2 && ptr)
                    delete ptr ;
                   else --*ref_counter ;
               }
 
        T* ptr;
        int *ref_counter;
};
 
struct Data
{
    int a=23;
    int getVal()const{return val;}
    void setVal(int v){val=v;}
    private: int val=123;
};
 
 
int main()
{
const Data data;
CoWptr<Data> cowPtr1(data);
 
cout<<cowPtr1.readData().getVal()<<endl;
cowPtr1.writeData().setVal(2345);
cout<<cowPtr1.readData().getVal()<<endl;
 
return 0;
}
Добавлено через 4 минуты
Спать надо идти) Завтра подумаю, может как-то через прокси и преобразование попробовать отловить намерение писать или читать (вызывать константный или неконстантный метод у содержимого) непосредственно на интерфейсе указателя - операторах * и -> ...
Но боюсь, не выйдет. А через методы чтения/записи кривовато выходит.
Однако лих ваш препод, если совместил 2 таких темы. Бог ему не судья. Он его уже и так обидел. Имхо.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13214 / 6847 / 1825
Регистрация: 18.10.2014
Сообщений: 17,333
21.04.2020, 04:21
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Задача:
Написать простенький аналог shared_ptr применяя парадигму Copy on Write.
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Эту простенькую задачку нам дали, для того чтобы мы изучили CoW на вот таком небольшом примере, а после этого нужно будет применить СoW к строкам... Боюсь, что если я сейчас не могу разобраться, то к строкам можно даже не притрагиваться)
Вы взяли какой-то плохой и странный пример. Не может быть никакого shared_ptr c CoW в принципе. Осмысленное CoW возможно только в том случае, когда объект-владелец полностью перехватывает и реализует весь интерфейс [модифицирующего] доступа к данным. То есть объект-владелец никак не может быть универсальным "просто указателем".

Хорошим кандидатом/примером CoW может служить, например, std::string. std::string, как вы сами понимаете, можно реализовывать через reference-counting и CoW. В такой ситуации каждый std::string объект - это фактически вариация "shared_ptr". Но это именно std::string: всю работу с подлежащими данными вы выполняете через интерфейс std::string. То есть этот "shared_ptr" пропускает всю работу с данными через себя. Он точно знает, когда происходит изменение вверенных ему данных (кроме доступа через data() разумеется), и может разумно принимать решение о том, когда нужно сделать CoW.

В случае абстрактного универсального shared_ptr ничего этого нет. И никакого осмысленного CoW тоже быть не может. Работа с данными, вверенными shared_ptr, возможна только напрямую (примерно как если бы мы с std::string всегда работали только через data()), что полностью исключает какое-то разумное CoW.

То есть максимум, что вы сможете сделать - это по прямому запросу пользователя "отщеплять" конкретный экземпляр вверенных shared_ptr данных в уникальную копию, перед тем как делать write. Называть такое ручное "copy before write" термином CoW - профанация.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
21.04.2020, 11:02
Ночью надо спать.
Цитата Сообщение от IGPIGP Посмотреть сообщение
if(*ref_counter<2 && ptr)
указатель в ноль неплохо бы сбросить. Хотя и проверка на ноль и удаление нуле не проблема. А с указателем пока не вижу выхода. Всё едино и новый оберточный не реагирует на константность методов класса данных. То есть проблема воспроизводится рекурсивно. Интересно, существует ли шаблонный способ выяснить вызван ли на типе typename T константный или обычный метод...
Так или иначе, нельзя пытаться заставить Смартройнтер знать что-то специфическое о интерфейсе класса данных. Стало быть придётся использовать свои методы навродь: readWal/writeVal и readByPointer/writeByPointer а семантику указателей использовать на месте вызова, например. Не то что бы не здоровьем это попахивает, но непривычно - точно. И во всяком случае, это не то, что стоило совмещать с темой смарта с подсчётом ссылок. Это сама по себе не простая тема требует нового взгляда на весь копирующий, конструирующий/разрушающий функционал. Новый уровень понимания владения. Тема заслуживающая уважения, а следовательно - времени и усилий.
Привет преподу)
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
21.04.2020, 11:13
Цитата Сообщение от Ailuropoda Посмотреть сообщение
Написать простенький аналог shared_ptr применяя парадигму Copy on Write.
механизмы подобного рода сложны.
"написать простенький" не получится.

я это пишу, как программист, который уже делал подобные вещи.

Цитата Сообщение от Ailuropoda Посмотреть сообщение
Не могу понять в какой момент дергать функцию создания данных и как правильно это сделать.
твоя проблема сейчас не с реализацией.
а с пониманием дизайна вообще.

свой механизм я назвал "версионный указатель" (version_ptr)
почему версионный?
потому что через него можно получать доступ к данным,
которые могут быть как в старой версии (до модификации), так и в новой (после модификации)

дизайн использования:

C++
1
2
3
4
version_ptr<data> smart = make_version_ptr(args...);
 
const data& cur1 = smart.rd(); // <--- тоступ для чтения. 
data& cur2 = smart.wr(); // <--- тоступ для модификации.
в примере выше rd() сокращение от read - доступ только для чтения. не приводит к созданию новой копии данных.

wr() сокращение от write - доступ для записи. приводит к расщеплению данных.

при этом cur1 и cur2 существуют одновременно
их можно получить, например, в двух разных потоках
один поток все временя только читает.
а другой поток выполняет запись.

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

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

что бы все они могли работать одновременно.

ps
персонаж под ником Fulcrum_013 похоже опять забыл принять свои лекарства.
не предавай слишком большое значение тому, что он пишет.

Добавлено через 41 секунду
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Вы взяли какой-то плохой и странный пример. Не может быть никакого shared_ptr c CoW в принципе. Осмысленное CoW возможно только в том случае, когда объект-владелец полностью перехватывает и реализует весь интерфейс [модифицирующего] доступа к данным. То есть объект-владелец никак не может быть универсальным "просто указателем".
прохладная история, Бро.
0
21.04.2020, 11:50

Не по теме:

Цитата Сообщение от hoggy Посмотреть сообщение
прохладная история, Бро.
Просто факты, прохладные, как аксиомы геометрии, бро...

0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
21.04.2020, 12:01
Ailuropoda, тут начинается взаимная диагностика, это к сожалению нормально. Однако все мы тут не раз слышали о указателях с обратными ссылками и это легко представить. Вы, желая изменить текст/рисунок или даже оглавление книги и вместо создания собственного поля деятельности, - говорите всем остальным смартам (у каждого из них есть ваш адрес - обратная ссылка) - а не пошли бы вы в магазин за новой книжкой. А уж если контейнеру нужно вычистить что-то (вектор не любит указателей внутрь - то есть к нему нужно итератором и его методом типа erase обращаться) то смарт должен контейнеры stl знать назубок. Или забыть в одночасье и написать свои.
Но время от времени мы это слышим и надо сказать, это полезно для раздумий о смысле самого смысла как такового в самом общем смысле этого смысла.
Что касается CoW как инструмента этики (выше уже это было затронуто), то в развитие темы скажу, что не только разные потоки требуют этики. Этики требуют сами объекты. Ведь мы разделяем и властвуем изгоняя глобальные объекты и неконтролируемые зависимости не для того чтобы создать (лержитесь - новый термин) модель разделяемого представления (корова CoW - это оно, как разновидность), в которой каждый субъект понятия не имеет кто последний и как модифицировал данные. Представьте, что в классе есть указатель и один из коллективных любителей молока взял и сбросил его в ноль. Понос у него сегодня. Болеть будут все кто попытается разыменовать этот указатель.
Цитата Сообщение от Экспертов по разделяемому представлению и этике
Спит Змей Горыныч (три головы - три относительно умных клиента), а перед ним стоит бутылка водки (разделяемый объект). Одна голова открывает глаз. Потом тихо поднимается и берёт общею рукой бутылку. В это время поднимается вторая голова и тоном не допускающим возражений тихо говорит: -"Пополам или заору". Оба выпивают по половине и просыпается третья голова. Она смотрит больными глазами то на пустую бутылку (невалидный объект), то на "своих пацанов" и орёт: - "Как пить так без меня, а как блевать - так втроём?!"
Как видите, эта история окончилась счастливо так как у каждого совладельца есть глаза чтобы увидеть (протестить) объект перед вызовом метода drink(). Однако, это дорого, тестировать любое поле. Да и критериев немерено. Водку же можно не только честно выпить (этическое хакерство). Можно же и на уксус заменить... или что-то более летальное но менее отличное от оригинала (палёная водка - раствор метилового спирта в болотной воде). А тут уже химлаборатория нужна. В общем, этика требуется и в одном потоке исполнения.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
21.04.2020, 13:18
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Просто факты, прохладные, как аксиомы геометрии, бро...
твои "просто факты" - просто балабольство неосилятора.

вот это - факт.
Цитата Сообщение от hoggy Посмотреть сообщение
я это пишу, как программист, который уже делал подобные вещи.
я уже давно заметил.
ты используешь слово "невозможно" для вполне осуществимых вещей.
даже после того, как тебя ткнут рожей в рабочий код.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
21.04.2020, 13:18

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 с чистой...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
Модель по догадкам
anaschu 25.08.2026
Прошло две недели. Я уже рассказывал, как разговаривал с сотрудниками у сортировки и как понял, что главная ветка — не про приёмку, а про отбор. Но тогда я думал, что понял механику. На этой неделе я. . .
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru