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

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

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

Сообщений: 3258


Просмотр профиля
« : Ноябрь 18, 2016, 00:09 »

Поиграю-ка я в Igors'а.
Допустим, у нас есть класс, который овнит объекты. Например, содержит дерево внутри себя. Чтоб далеко не ходить - QTextDocument::rootFrame. Внутри, понятное дело, мы заюзаем std::unique_ptr. А снаружи? Отдавать голый указатель? Попахивает 90ми годами. Или всё-таки внутри делаем std::shared_ptr и отдаём копию? Но смысл иметь фрейм без документа? Тогда weak? Но ведь лочить неудобно, да и всё равно овнершип "размазывается".
Как это делается в "modern c++"?
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #1 : Ноябрь 18, 2016, 04:36 »

как то так:

Код:
xml::document doc("blablabla.xml");
xml::node root = doc.child("root");
xml::node example = root.child("example");

суть в том, что node - легковесный интерфейсный класс,
который держит под капотом либо std::weak_ptr,
либо std::shared_ptr (в зависимости от потребностей задачи).

однако пользователи работают с объектами,
как с обычными автоматическими переменными на стеке.
и не заморачиваются деталями реализации.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #2 : Ноябрь 18, 2016, 10:57 »

Может ли нод жить без документа? В случае QJsonValue, очевидно, может.
В случае QTextFrame вроде нет.
Далее, QTextFrame может изменяться. Красиво ли, что изменяется наша копия? Или она должна детачить?
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #3 : Ноябрь 18, 2016, 11:16 »

А что такого в изменении данных? Улыбающийся
ИМХО, нужно просто знать пользователю, как с этим работать. Конечно, это требует немного большей компетенции Улыбающийся
Например, ситуация
Код
C++ (Qt)
QVector<int> vec = {1, 2, 3};
const int &a = vec.at(3);
vec << 4;
Или ситуация, когда мы в одном месте коннектим спинбокс к слайдеру, например, а в другом пользуемся указателем на виджет слайдера. Ясное дело, что может изменяться объект, адрес которого содержит указатель...
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #4 : Ноябрь 18, 2016, 12:11 »

А что такого в изменении данных? Улыбающийся
ИМХО, нужно просто знать пользователю, как с этим работать. Конечно, это требует немного большей компетенции Улыбающийся
Например, ситуация
Код
C++ (Qt)
QVector<int> vec = {1, 2, 3};
const int &a = vec.at(3);
vec << 4;
Или ситуация, когда мы в одном месте коннектим спинбокс к слайдеру, например, а в другом пользуемся указателем на виджет слайдера. Ясное дело, что может изменяться объект, адрес которого содержит указатель...

Так ведь указатель же. В примере с нодами мы об указателях вообще ничего не знаем, мы держим копию. То, что при изменении одной копии меняется другая - странно.
Вот, собственно структура.
Код:
class Document;
struct Item
{
     Document *document;
     Item *parent;
     std::vector<Item *> children;
};

class Document
{
     Item *rootItem;
};
Если заюзать shared_ptr, то придётся использовать богомерзкий enable_shared_from_this для Document, который на корню убивает возможность не пользовать shared_ptr.
Мне кажется, или в std не хватает "глупого" указателя, который суть итератор без возможности итерации?
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #5 : Ноябрь 18, 2016, 12:43 »

Если я вас правильно понял, то, кажется, здесь будет уместен механизм-обёртка подсчёта ссылающихся на одни и те же данные нодов.
При попытке изменения данных производится глубокое копирование данных и их изменение.

Подобный механизм в контейнерах Qt реализован.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #6 : Ноябрь 18, 2016, 12:49 »

Если я вас правильно понял, то, кажется, здесь будет уместен механизм-обёртка подсчёта ссылающихся на одни и те же данные нодов.
При попытке изменения данных производится глубокое копирование данных и их изменение.

Подобный механизм в контейнерах Qt реализован.

Но мне не нужен подсчёт нодов. У меня есть четкий овнер нодов - это Document. Внутри дерева нодов тоже есть иерархия - парент владеет детьми.
Вопрос, как это сделать в modern c++, без голых указателей.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #7 : Ноябрь 18, 2016, 14:12 »

А, я понял.
Для меня возврат глупого указателя не пахнет 90ми Улыбающийся
С weak_ptr не работал - интересно будет послушать мнение других.
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #8 : Ноябрь 18, 2016, 15:51 »

Может ли нод жить без документа? В случае QJsonValue, очевидно, может.
В случае QTextFrame вроде нет.
Далее, QTextFrame может изменяться. Красиво ли, что изменяется наша копия? Или она должна детачить?

если нода не может жить без документа - держит под капотом std::weak_ptr
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #9 : Ноябрь 18, 2016, 16:06 »

Если заюзать shared_ptr, то придётся использовать богомерзкий enable_shared_from_this для Document, который на корню убивает возможность не пользовать shared_ptr.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #10 : Ноябрь 18, 2016, 18:15 »

Набросок, но рабочий. Улыбающийся

main.cpp
Код
C++ (Qt)
#include <iostream>
#include "tree.h"
 
using namespace std;
 
int main()
{
cout << "Start..." << endl;
 
Document doc( "Test doc" );
 
ItemRef r = doc.root();
ItemRef c1 = r.new_child( "Child 1" );
ItemRef c2 = c1.new_child( "Child 2" );
cout << "Child2 is_valid: " << c2.is_valid() << " docname: " << c2.doc_name() << endl;
 
r.remove_child( c1 );
cout << "Child2 is_valid: " << c2.is_valid() << " docname: " << c2.doc_name() << endl;
 
return 0;
}
 

tree.h
Код
C++ (Qt)
#ifndef TREE_H
#define TREE_H
 
#include <string>
#include <memory>
 
using std::string;
using std::shared_ptr;
using std::weak_ptr;
using std::make_shared;
 
class Item;
 
class ItemRef
{
public:
bool is_valid() const;
string name() const;
void set_name( const string &val );
string doc_name() const;
 
ItemRef new_child( const string &name );
void remove_child( const ItemRef &item );
 
private:
ItemRef() {}
ItemRef( const shared_ptr<Item> &item ) : m_ptr( item ) {}
 
private:
weak_ptr<Item> m_ptr;
 
friend class Document;
};
 
class Document
{
public:
Document( const string &name = string() );
 
ItemRef root() const { return ItemRef( m_root ); }
string name() const { return m_name; }
 
private:
shared_ptr<Item> m_root;
string m_name;
};
 
#endif // TREE_H
 

tree.cpp
Код
C++ (Qt)
#include "tree.h"
#include <deque>
#include <algorithm>
 
struct Item
{
explicit Item( Document &document ) : m_document( document ) {}
explicit Item( const shared_ptr<Item> &parent, const string &name = string() ) : m_document( parent->m_document ), m_parent( parent ), m_name( name ) {}
explicit Item( Document &document, const string &name ) : m_document( document ), m_name( name ) {}
 
void remove_child( const shared_ptr<Item> &item )
{
m_children.erase( std::remove_if( m_children.begin(), m_children.end(), [item]( const shared_ptr<Item> &c ) { return c == item; } ), m_children.end() );
}
 
Document &m_document;
weak_ptr<Item> m_parent;
std::deque<shared_ptr<Item>> m_children;
 
string m_name;
};
 
 
bool ItemRef::is_valid() const
{
return m_ptr.lock() != nullptr;
}
 
string ItemRef::name() const
{
auto p = m_ptr.lock();
return p? p->m_name : string();
}
 
void ItemRef::set_name( const string &val )
{
auto p = m_ptr.lock();
if( p ) p->m_name = val;
}
 
string ItemRef::doc_name() const
{
auto p = m_ptr.lock();
return p? p->m_document.name() : string();
}
 
ItemRef ItemRef::new_child( const string &name )
{
auto p = m_ptr.lock();
if( p )
{
shared_ptr<Item> item = make_shared<Item>( p, name );
p->m_children.push_back( item );
return ItemRef( item );
}
return ItemRef();
}
 
void ItemRef::remove_child( const ItemRef &item )
{
auto p = m_ptr.lock();
if( p )
p->remove_child( item.m_ptr.lock() );
}
 
 
Document::Document( const string &name ) :
m_root( make_shared<Item>( *this ) ),
m_name( name )
{
}
 
« Последнее редактирование: Ноябрь 18, 2016, 22:22 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Ноябрь 19, 2016, 08:53 »

Цитировать
Shared pointer
Manages the storage of a pointer, providing a limited garbage-collection facility, possibly sharing that management with other objects.
Да, объект будет шариться, но про его члены никто ничего не обещал, на то оно и limited. Это в пытоне или жабе объект живет пока используется хоть один его член - что впрочем не бесплатно и приносит не только выгоды. А здесь такого нет, поэтому искать пятый угол не стоит.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #12 : Ноябрь 30, 2016, 15:42 »

Old
Ну да, что-то такое на чистых плюсах и должно быть, спасибо.
К сожалению, для Qt с их любовью к голым указателям, не очень подходит.

Igors
К чему это?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #13 : Ноябрь 30, 2016, 16:18 »

К сожалению, для Qt с их любовью к голым указателям, не очень подходит.
Это не любовь, это legacy. Улыбающийся
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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