Russian Qt Forum
Март 29, 2024, 05:20 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4 5 6   Вниз
  Печать  
Автор Тема: Qt + вумные указватели  (Прочитано 26649 раз)
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #30 : Февраль 19, 2020, 17:47 »

Ну или можно сделать что-то типа
Код:
{
    std::vector<ItemHolder> toDelete;
    for (const auto &item: view->selectedItems()) {
        toDelete.push_back(item->parent()->take(item));
    }
} // вот тут всё разрушилось

По сути, мы "раздербанили" дерево на отдельные айтемы сохраняя инвариант.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #31 : Февраль 19, 2020, 17:52 »

Кстати начиная с какой-то версии плюсов это не нужно, можно просто std::swap звать, потому что теперь можно пегегружать std::swap и юзерский swap не нужен.

Про такое слышал, но когда библиотека писалась, это может ещё не работало, точно не помню. Поэтому оставил по старинке, для надёжности. Подожду, когда это в С++20 утрясётся, там swap уже customization point обзывают.

У нас кстати вот есть точно такой кусок кода - объекты в конструкторе себя регистрируют в глобальном кэше, при удалении дерегистрируют.

Я вообще склоняюсь к тому, что объект не должен знать, под каким типом владения он находится, и не может сам создавать ассоциативные связи с собой, если ему не дали для этого достаточно данных. Т.е. shared_from_this не должно быть у объекта, максимум  weak_from_this, да и то под большим вопросом. Это сильно привязывает класс к типу владения. На Хабре недавно было: "Сказ об опасном std::enable_shared_from_this...". Без этих xxxx_from_this писать сложнее, но, возможно, правильнее Улыбающийся.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #32 : Февраль 19, 2020, 18:27 »

Я вообще склоняюсь к тому, что объект не должен знать, под каким типом владения он находится, и не может сам создавать ассоциативные связи с собой, если ему не дали для этого достаточно данных. Т.е. shared_from_this не должно быть у объекта, максимум  weak_from_this, да и то под большим вопросом. Это сильно привязывает класс к типу владения. На Хабре недавно было: "Сказ об опасном std::enable_shared_from_this...". Без этих xxxx_from_this писать сложнее, но, возможно, правильнее Улыбающийся.

Вот тут полностью соглашусь, этот std::enable_shared_from_this выглядит очень убого и непродуманно.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #33 : Февраль 19, 2020, 21:49 »

Идея интересная, но проблема в том, что итераторы вектора не стабильны - при добавлении айтема в середину итераторы поедут.
Так-то можно просто в сам айтем положить его "позицию" (итератор или индекс) в паренте но см выше.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #34 : Февраль 19, 2020, 23:15 »

Идея интересная, но проблема в том, что итераторы вектора не стабильны - при добавлении айтема в середину итераторы поедут.
Так-то можно просто в сам айтем положить его "позицию" (итератор или индекс) в паренте но см выше.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.

Конечно нужен вектор, это ж айтем _модели_, а у нее должен быть доступ к строке за О(1)
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #35 : Февраль 20, 2020, 09:25 »

Конечно нужен вектор, это ж айтем _модели_, а у нее должен быть доступ к строке за О(1)

Тогда можно так

Код
C++ (Qt)
using Items = ::std::list< Item >;
using ItemForIndex = ::std:vector< Items::iterator >
 
Items m_items;
ItemForIndex m_item_for_index;
 
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #36 : Февраль 20, 2020, 09:49 »

Пробовал. И уже дважды постил на форуме. Получается отлично.

Там случайно в методе createChild не ошибка?

Код
C++ (Qt)
   ItemPointer createChild(qsizetype row = -1)
   {
       insert(row == - 1 ? childCount() : row, std::make_unique<Derived>());
       return ItemPointer(m_children.back().get());
   }
 

При любом row возвращается последний из элементов. Не должно быть хотя бы так?

Код
C++ (Qt)
   ItemPointer createChild(qsizetype row = -1)
   {
       insert(row == - 1 ? childCount() : row, std::make_unique<Derived>());
       return child(row);
   }
 
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #37 : Февраль 20, 2020, 10:26 »

Да, действительно=) Сказывается отсутствие тестов, там еще и в remove был assert неправильный=)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #38 : Февраль 20, 2020, 14:06 »

И будет падать независимо от того, вумные указатели в векторе или обычные.
Единственное хорошее в вумных указателях - это их валидность. Напр при использовании либы ViTech тамошний weak стал бы корректным null при удалении парента (ну это я так думаю). А если мне все равно рысью бегать и следить чтобы никто его не замочил - так проще голые.

Насколько оправдано использование вектора здесь? Разве нужен рандомный доступ?
Преимущества std::list и его итераторов, которые не портятся со временем), здесь явно.
Ну хорошо, допустим под видом обсервера Вы храните итератор list (как я понял). Но как Вы обеспечите его валидность? Др словами
Код
C++ (Qt)
ItemPointer p = ...;  // получили валидный айтем
...
// тут может происходить что угодно, возможно айтем грохнут
---
if (!p.isNull()) {
 
Сработает эта проверка?

Немного о другом. Я думал что юники класть в контейнер нельзя (начинает вякать что нельзя копировать). Но судя по коду Авварон - оказывается можно! Поясните подробности. Спасибо
 
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #39 : Февраль 20, 2020, 14:35 »

Код
C++ (Qt)
...
// тут может происходить что угодно, возможно айтем грохнут
---
 

Айтем грохнуть может только родительский айтем. ItemPointer в данном случае не должен предоставлять такую возможность.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #40 : Февраль 20, 2020, 16:59 »

Айтем грохнуть может только родительский айтем. ItemPointer в данном случае не должен предоставлять такую возможность.
Как же он не даст если parent() метод public? А если не так - то как "ваще" удалять? (такие очевидные вопросы и озвучивать неудобно Улыбающийся)
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #41 : Февраль 20, 2020, 17:07 »

Немного о другом. Я думал что юники класть в контейнер нельзя (начинает вякать что нельзя копировать). Но судя по коду Авварон - оказывается можно! Поясните подробности. Спасибо


В кутешные (с коровой, COW) нельзя потому что любая неконстантная операция потенциально может детачить, то есть делать deep copy, то есть требует наличия конструктора копирования у контейнера и, как следствие, у T.
Контейнеры стандартной библиотеки используют конструктор копирования Т только в своем конструкторе копирования. То есть пока пользователь явно не захочет скопировать контейнер, компилятору глубоко по барабану, копируется Т или нет.
Вектор юников можно спокойно мувать в функцию или возвращать из функции по значению.
То же самое относится и к конструктору по умолчанию у Т, некоторые кутешные контейнеры требуют его наличия, когда стандартным пофиг.
« Последнее редактирование: Февраль 20, 2020, 17:16 от Авварон » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #42 : Февраль 20, 2020, 17:16 »

Др словами
Код
C++ (Qt)
ItemPointer p = ...;  // получили валидный айтем
...
// тут может происходить что угодно, возможно айтем грохнут
---
if (!p.isNull()) {
 
Сработает эта проверка?

Не сработает и не должна.
Вас _уже_ защитили от явного вызова delete использованием обсервера. Просто так взять и "грохнуть" айтем не получится - надо айем "достать" из дерева.
Причем после "доставания" можете считать, что исходный ItemPointer невалидный - теперь у вас новый хэндл, который вернул take() и вы теперь пользуетесь этим хэндлом.
Как std::vector::erase(), один в один. Вызвали деструктивную операцию - извольте обновить итераторы.
Вы упорно пытаетесь хранить стейт (хотя бы выше по стеку) и находите всё новые и новые баги ругая всё подряд. Не приходило в голову, что проблема не в "вумных" указателях, которые "недостаточно мощны", а в хранении стейта?
Вы пытаетесь найти решение проблемы которую сами себе создали - "а вот у меня всё от всего зависит и всё на всё ссылается, какую бы таблетку мне принять, чтобы голова не болела".
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #43 : Февраль 20, 2020, 17:19 »

Тогда можно так

Код
C++ (Qt)
using Items = ::std::list< Item >;
using ItemForIndex = ::std:vector< Items::iterator >
 
Items m_items;
ItemForIndex m_item_for_index;
 

Так всё равно же вынимание за О(N).

Я хочу напомнить, что "вынимание" айтема из середины вектора само по себе O(N), так что линейный поиск не так страшен.
А вот range "вынимание" было бы интересно добавить. Но это ж надо делать итераторы по айтему=)
« Последнее редактирование: Февраль 20, 2020, 17:21 от Авварон » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #44 : Февраль 21, 2020, 12:05 »

Вектор юников можно спокойно мувать в функцию или возвращать из функции по значению.
Т.е. сработает лишь конструктор перемещения (а он у юника имеется)
То же самое относится и к конструктору по умолчанию у Т, некоторые кутешные контейнеры требуют его наличия, когда стандартным пофиг.
Наверно речь идет о "некоторых" контейнерах, напр std::vector.resize() потребует дефаултный конструктор

Вы упорно пытаетесь хранить стейт (хотя бы выше по стеку) и находите всё новые и новые баги ругая всё подряд.
Не совсем так, сохранить state я не пытаюсь, против изменения итераторов возражений нет. НО валидность (isNull) должна быть, иначе это дискредитирует саму идею вумных указателей. Ну в самом деле, нафиг они нужны если имею все те же проблемы голых?

Не сработает и не должна.
А мне кажется обеспечить валидность можно. И "без великов". Попробуем так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<std::shared_ptr<CItem>> m_children;
};
 
Конечно юзать шаред не есть хорошо. Но мы постараемся зажать его в рамках класса. Здесь так не выходит - никак не объявить root (или take). Поэтому предлагаю так
Код
C++ (Qt)
struct CIem {
typedef std::weak_ptr<CItem>  TItemPtr;
...
 
private:
TItemPtr m_parent;
std::vector<TItemPtr> m_children;
 
typedef std::list<std::shared_ptr<CItem>> TList;
TList m_list;
TList::iterator m_iterator;
};
 
m_list хранит шаред чилдренов, а если это root - то и шаред на себя. Тогда
Код
C++ (Qt)
// static
bool CItem::DeleteItem( TItemPtr itemPtr )
{
 auto item = itemPtr.lock();
 if (!item) return false;
 auto parent = item->parent().lock();
 if (parent)
   parent->removeChild(item.get());
 else
  item->m_list.erase(m_iterator);
 return true;
}
 
CItem::~CItem( void )
{
 while (m_children.size())
   DeleteItem(m_children.front());
}
Попинайте
« Последнее редактирование: Февраль 21, 2020, 12:07 от Igors » Записан
Страниц: 1 2 [3] 4 5 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.34 секунд. Запросов: 23.