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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: std::shared_ptr и множественное наследование  (Прочитано 9683 раз)
RedDog
Гость
« : Апрель 24, 2015, 11:28 »

Как правильно создать shared_ptr если класс унаследован от нескольких родителей:

Код:
class CPingAdmin : public QObject, xmlrpc_c::method
{
    Q_OBJECT
public:
    CPingAdmin(){}
};

Пытаюсь создать shared_ptr:
Код:
std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
            = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );
Выводит кучу ошибок в недрах stl.
/usr/include/c++/4.7/ext/new_allocator.h:110: ошибка: 'xmlrpc_c::method' is an inaccessible base of 'CPingAdmin'
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #1 : Апрель 24, 2015, 11:41 »

Наследуешься от xmlrpc_c::method, а тип пойнтера xmlrpc_c::methodPtr.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
RedDog
Гость
« Ответ #2 : Апрель 24, 2015, 11:45 »

Если я делаю вот так:

Код:
class CPingAdmin : public xmlrpc_c::method
{
public:
    CPingAdmin(){}
};

std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
            = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );
То все нормально работает.
Но мне нужен в данном случае Qt-шный сигнал из класса, поэтому пришлось добавить QObject.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #3 : Апрель 24, 2015, 12:05 »

class CPingAdmin : public QObject, public xmlrpc_c::method
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
RedDog
Гость
« Ответ #4 : Апрель 24, 2015, 13:07 »

class CPingAdmin : public QObject, public xmlrpc_c::method
Спасибо, помогло.
Но появилась новая проблема - коннект сигнала к слоту:

Код:
    std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
            = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );

    QObject::connect( PingAdmFunc.get(), SIGNAL( PingReceived() ),
                      this, SLOT( on_admPing() ) );

Цитировать
QtCore/qobject.h:215: ошибка: no type named 'Object' in 'struct QtPrivate::FunctionPointer<const char*>'
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Апрель 24, 2015, 14:00 »

Но появилась новая проблема - коннект сигнала к слоту:
Цитировать
QtCore/qobject.h:215: ошибка: no type named 'Object' in 'struct QtPrivate::FunctionPointer<const char*>'
Нужно кросс-привестись
Код
C++ (Qt)
 
std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
           = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );
 
CPingAdmin * pingAdmin = dynamic_cast <CPingAdmin *> (PingAdmFunc.get());
QObject::connect(pingAdmin , SIGNAL( PingReceived() ),  this, SLOT( on_admPing() ) );
 

[OFF]
Написано мало, но вот понять уже не так просто - заслуга "xmlrpc_c::methodPtr". И "крутые" скобки с пробелами  этого не компенсируют  Улыбающийся
[/OFF]
Записан
Nimbus
Гость
« Ответ #6 : Апрель 24, 2015, 14:32 »

Если я делаю вот так:

Код:
class CPingAdmin : public xmlrpc_c::method
{
public:
    CPingAdmin(){}
};

std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
            = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );
То все нормально работает.
Но мне нужен в данном случае Qt-шный сигнал из класса, поэтому пришлось добавить QObject.

std::make_shared вызывает placement new, и затем конструктор и переданными ему аргументами.
Зачем вы два раза создаёте объект xmlrpc_c::method?
Правильно так:
Код:
std::shared_ptr< xmlrpc_c::method > PingAdmFunc = std::make_shared< CPingAdmin >();
Или
Код:
std::shared_ptr< CPingAdmin > PingAdmFunc = std::make_shared< CPingAdmin >();
В зависимости от того, какого типа указатель вам нужен в итоге.
std::make_shared и служит для того, чтобы самому не вызывать оператор new, а заодно и расположить объект и его счётчик ссылок в одном куске памяти + ещё какие-то там оптимизации.
Заодно советую почитать про enable_shared_from_this.
Записан
RedDog
Гость
« Ответ #7 : Апрель 24, 2015, 15:01 »

std::make_shared вызывает placement new, и затем конструктор и переданными ему аргументами.
Зачем вы два раза создаёте объект xmlrpc_c::method?
Правильно так:
Код:
std::shared_ptr< xmlrpc_c::method > PingAdmFunc = std::make_shared< CPingAdmin >();
Или
Код:
std::shared_ptr< CPingAdmin > PingAdmFunc = std::make_shared< CPingAdmin >();
В зависимости от того, какого типа указатель вам нужен в итоге.
std::make_shared и служит для того, чтобы самому не вызывать оператор new, а заодно и расположить объект и его счётчик ссылок в одном куске памяти + ещё какие-то там оптимизации.
Заодно советую почитать про enable_shared_from_this.
Так не работает, если внимательно посмотреть сто создается в шареде xmlrpc_c::methodPtr а не xmlrpc_c::method
Записан
Nimbus
Гость
« Ответ #8 : Апрель 24, 2015, 16:02 »

Так не работает, если внимательно посмотреть сто создается в шареде xmlrpc_c::methodPtr а не xmlrpc_c::method
Окей, тогда из текста ошибки ('xmlrpc_c::method' is an inaccessible base of 'CPingAdmin'), понятно, что вы наследуетесь приватно от xmlrpc_c::method. Отсюда и проблема.

то есть нужно так

Код:
class CPingAdmin : public QObject, public xmlrpc_c::method
{
    Q_OBJECT
public:
    CPingAdmin(){}
};
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Апрель 24, 2015, 16:24 »

Код:
std::shared_ptr< xmlrpc_c::methodPtr > PingAdmFunc
            = std::make_shared< xmlrpc_c::methodPtr >( new CPingAdmin() );
То все нормально работает.
Но мне нужен в данном случае Qt-шный сигнал из класса, поэтому пришлось добавить QObject.

std::make_shared вызывает placement new, и затем конструктор и переданными ему аргументами.
Зачем вы два раза создаёте объект xmlrpc_c::method?
Правильно так:
Код:
std::shared_ptr< xmlrpc_c::method > PingAdmFunc = std::make_shared< CPingAdmin >();
Поясните такие моменты

1) Зачем здесь make_shared? Почему не просто
Код:
std::shared_ptr< xmlrpc_c::method > PingAdmFunc(new CPingAdmin);
Вообще какие преимущества дает make_shared? (по сравнению с просто созданием шареного указателя)

2)  А как же у ТС откомпилилось - ведь конструктора xmlrpc_c::method принимающего указатель наверняка нет. Что же произошло?

Спасибо
« Последнее редактирование: Апрель 24, 2015, 16:26 от Igors » Записан
Nimbus
Гость
« Ответ #10 : Апрель 24, 2015, 16:38 »


1) Зачем здесь make_shared? Почему не просто
Код:
std::shared_ptr< xmlrpc_c::method > PingAdmFunc(new CPingAdmin);
Вообще какие преимущества дает make_shared? (по сравнению с просто созданием шареного указателя)
Ну легко же нагугливается.

2)  А как же у ТС откомпилилось - ведь конструктора xmlrpc_c::method принимающего указатель наверняка нет. Что же произошло?
Видимо, я наивно предположил, что xmlrpc_c::methodPtr -- это typedef method * methodPtr внутри класса xmlrpc_c::method.
Как оказалось, это отдельный класс, как раз принимающий в конструктор указатель на xmlrpc_c::method, что автор и продемонстрировал. Приватное наследование от этого класса как раз и привело к такой ошибке.

Спасибо
Не за что ;-)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Апрель 25, 2015, 12:04 »

Ну легко же нагугливается.
Мдааа... Аргументация впечатляет
Цитировать
The difference is that std::make_shared performs one heap-allocation, whereas calling the std::shared_ptr constructor performs two.
Как говорится "вспомнила баба как девкой была". Как c QString - так никто число malloc не считает (хоть их там тьма), а тут во как - аж 2(!) вместо одного, ай-яй-яй. Дальше еще интереснее
Цитировать
Since there there's only one allocation, the pointee's memory cannot be deallocated until the control block is no longer in use. A weak_ptr can keep the control block alive indefinitely.
В общем я так понял что weak_ptr не обещает корректности если шареный указатель создан через make_shared. Не слабо!

В общем еще раз убедился что изучать std надо в меру. 
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #12 : Апрель 25, 2015, 12:56 »

Цитировать
Since there there's only one allocation, the pointee's memory cannot be deallocated until the control block is no longer in use. A weak_ptr can keep the control block alive indefinitely.
В общем я так понял что weak_ptr не обещает корректности если шареный указатель создан через make_shared. Не слабо!

поведение weak_ptr вполне себе корректно.


под капотом есть два счетчика: количество сильных ссылок и слабых.
кроме этого - указатель на сам объект-ресурс смарт-поинтера.

1.
случай с двумя аллокациями:
Код:
shared_ptr<resource> sm (new resource);

имеем два указателя: на счетчики, и на объект-ресурс.

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

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

таким образом, последний шаред_птр грохнет объект-ресурс, и освободит его память.
последний смарт-поинтер (шаред или вик) грохнет объект-счетчиков, и освободит  его память.

в случае, если в памяти где то будет оставаться живой шаред_птр,
то в памяти будет висеть и объект-ресурс, и объект-счетчиков.

в случае, если в памяти где то будет оставаться живой вик_птр,
то в памяти будет висеть объект-счетчиков.

недостатки:
две аллокации - две дорогостоящие операции выделея,
и две ещё более дорогостоящих операций по освобождению.

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


2.
случай с одной аллокацией:
Код:
shared_ptr<resource> sm = std::make_shared<resource>();

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

например, так:

Цитировать
[ [счетчик сильных ссылок][счетчик слабых ссылок][      [ресурс ]       ]
        sizeof(uint32_t)                 sizeof(uint32_t)         sizeof(resource)

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

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

аналогично первому случаю, последний шаред_птр грохнет объект-ресурс (позовет деструктор)

но пока счетчик слабых ссылок ещё не обнулился, нельзя удалять саму память.
иначе, вик_птр станут неконсистентными.

таким образом:
пока есть хотя бы один живой шаред_птр, то ресурс будет живой.
если нет ни одного шаред_птр, то ресурс будет уничтожен,
но сама память будет висеть до тех пор, пока ещё остается хотя бы один вик_птр.


резюмируя:

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

на мой взгляд, 2й способ работы со смарт-поинтерами предпочтительнее, нежели первый.
зная немножко, как оно там устроенно внутри, я понимаю, что 2й способ работает эффективнее.

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

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Апрель 25, 2015, 13:16 »

но если пренебречь этой эффективностью,
то с точки зрения использования особой разницы нет.
Я бы еще добавил про исключения... но не буду. Улыбающийся

Потому что, главная фраза темы уже сказана:
В общем еще раз убедился что изучать std надо в меру.  

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

Тему можно закрывать.  Смеющийся
« Последнее редактирование: Апрель 25, 2015, 13:25 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Апрель 25, 2015, 14:52 »

если нет ни одного шаред_птр, то ресурс будет уничтожен,
Понял, в случае make_shared зовется деструктор - а не delete

две аллокации - две дорогостоящие операции выделея,
и две ещё более дорогостоящих операций по освобождению.
Ну дорого/дешево познается в сравнении. Напр сколько здесь будет таких операций?
Код
C++ (Qt)
QStringList lst = QString("This is text").split(" ");
Улыбающийся А что-то типа QRegExp еще похлеще. Но ни разу не видел чтобы это хоть кого-то остановило, Напротив, задействовать QRegExp считается делом чести. Как-то слышал типа
Цитировать
А вы знаете - ведь скриптовые языки по скорости уже догоняют плюсы!
Наверное уже и обогнали  Улыбающийся
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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