Russian Qt Forum

Программирование => С/C++ => Тема начата: RedDog от Апрель 24, 2015, 11:28



Название: std::shared_ptr и множественное наследование
Отправлено: 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'


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Пантер от Апрель 24, 2015, 11:41
Наследуешься от xmlrpc_c::method, а тип пойнтера xmlrpc_c::methodPtr.


Название: Re: std::shared_ptr и множественное наследование
Отправлено: RedDog от Апрель 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.


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Пантер от Апрель 24, 2015, 12:05
class CPingAdmin : public QObject, public xmlrpc_c::method


Название: Re: std::shared_ptr и множественное наследование
Отправлено: RedDog от Апрель 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*>'


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Igors от Апрель 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]


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Nimbus от Апрель 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.


Название: Re: std::shared_ptr и множественное наследование
Отправлено: RedDog от Апрель 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


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Nimbus от Апрель 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(){}
};


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Igors от Апрель 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 принимающего указатель наверняка нет. Что же произошло?

Спасибо


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Nimbus от Апрель 24, 2015, 16:38

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

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

Спасибо
Не за что ;-)


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Igors от Апрель 25, 2015, 12:04
Ну легко же нагугливается (http://stackoverflow.com/a/20895705/2080453).
Мдааа... Аргументация впечатляет
Цитировать
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 надо в меру. 


Название: Re: std::shared_ptr и множественное наследование
Отправлено: _Bers от Апрель 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й способ работает эффективнее.

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


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Old от Апрель 25, 2015, 13:16
но если пренебречь этой эффективностью,
то с точки зрения использования особой разницы нет.
Я бы еще добавил про исключения... но не буду. :)

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

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

Тему можно закрывать.  ;D


Название: Re: std::shared_ptr и множественное наследование
Отправлено: Igors от Апрель 25, 2015, 14:52
если нет ни одного шаред_птр, то ресурс будет уничтожен,
Понял, в случае make_shared зовется деструктор - а не delete

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


Название: Re: std::shared_ptr и множественное наследование
Отправлено: navrocky от Апрель 28, 2015, 11:26
Ну мне пригодился make_shared, и даже не он а allocate_shared, когда я уперся в фрагментацию памяти. Выделялось несколько миллионов объектов по 100 байт. Пришлось городить пул и выделять через allocate_shared.