Russian Qt Forum

Программирование => С/C++ => Тема начата: lenny от Август 16, 2011, 10:58



Название: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 10:58
Глядя на прототипы методов Qt, не могу понять чем они руководствуются, всегда предполагал, что передача по константной ссылке - уход от копирования объекта, передача по ссылке - объект будет изменен, передача по указателю - указатель будет где-то сохранен.


Название: Re: Передача по ссылке или указателю?
Отправлено: Пантер от Август 16, 2011, 11:02
В чем вопрос?


Название: Re: Передача по ссылке или указателю?
Отправлено: brankovic от Август 16, 2011, 12:00
Глядя на прототипы методов Qt, не могу понять чем они руководствуются, всегда предполагал, что передача по константной ссылке - уход от копирования объекта, передача по ссылке - объект будет изменен, передача по указателю - указатель будет где-то сохранен.

так в Qt так и есть, какие именно прототипы смущают?


Название: Re: Передача по ссылке или указателю?
Отправлено: lit-uriy от Август 16, 2011, 12:19
Глядя на прототипы методов Qt, не могу понять чем они руководствуются
тыц (http://wiki.crossplatform.ru/index.php/Designing_Qt-Style_C%2B%2B_APIs)


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 13:27
Глядя на прототипы методов Qt, не могу понять чем они руководствуются
тыц (http://wiki.crossplatform.ru/index.php/Designing_Qt-Style_C%2B%2B_APIs)

Спасиб! Написано, что везде указатели при передаче модифицируемых параметров, пригляделся, вроде так и есть.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 16, 2011, 14:20
Спасиб! Написано, что везде указатели при передаче модифицируемых параметров, пригляделся, вроде так и есть.
Ну это скорее второе. А первое - указатель может быть NULL (и значит вызываемый это учтет), а ссылка нет

тыц (http://wiki.crossplatform.ru/index.php/Designing_Qt-Style_C%2B%2B_APIs)
Что-то в этом "тыц"е старательно замалчивается как они (с наглой мордой) передают/возвращают структуры по значению :) Надо понимать что и так ясно - этот стиль "уже хороший"


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 16, 2011, 14:48
старательно замалчивается как они (с наглой мордой) передают/возвращают структуры по значению
Можно поподробней?


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 16:04
Ну это скорее второе. А первое - указатель может быть NULL (и значит вызываемый это учтет), а ссылка нет
Тож верно, я обычно в учет это не беру, лишние проверки, да и указатель все равно может на мусор показывать.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 16:08
Можно поподробней?
Так почти все по значению возвращают.


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 16, 2011, 16:37
Так почти все по значению возвращают.
Вы про классы?
Так подавляющее большинство классов Qt классов - это умный указатель на QИмяКлассаPrivate.
Поэтому на самом деле тут данные не копируются, а передаётся указатель, просто скрыто от вас.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 16:55
Честно говоря не знал, надо в исходники заглянуть.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 17:02
Вы про классы?
Так подавляющее большинство классов Qt классов - это умный указатель на QИмяКлассаPrivate.
Поэтому на самом деле тут данные не копируются, а передаётся указатель, просто скрыто от вас.

А это как и где на это можно посмотреть? Механизм не понимаю.


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 16, 2011, 18:17
http://doc.qt.nokia.com/4.7/implicit-sharing.html
И что немаловажно: http://doc.qt.nokia.com/4.7/threads-modules.html#threads-and-implicitly-shared-classes


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 18:29
В чем вопрос?
Эт у меня мозг вскипел. Сижу, пишу и замечаю, что от меня требуют не константные указатели на не константные объекты и непонятно, что с ними делать будут, типа паранойя началась.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 16, 2011, 18:57
http://doc.qt.nokia.com/4.7/implicit-sharing.html
И что немаловажно: http://doc.qt.nokia.com/4.7/threads-modules.html#threads-and-implicitly-shared-classes
Здесь об этом немного, но понятно написано http://www.ibm.com/developerworks/ru/library/l-qt_2/
Насколько я понял, при модификации данных глубокое копирование все равно происходит. Или я чего-то не догоняю?


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 16, 2011, 19:32
Здесь об этом немного, но понятно написано
В смысле по-русски?

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

Соль в том что можно передавать по значению и:
1) если не будет изменений, то не будет лишнего копирования данных, т.е. фактически замена константной ссылке;
2) если будет изменение, то само всё копируется (не изменив оригинал) и об этом не нужно задумываться, т.е. фактически замена передачи по значению;
3) и кроме того это потоко-безопасно, то есть мы можем передавать такой класс как аргумент сигнала и неизменные данные дойдут до всех присоединённых слотов, даже если некоторые из них вызываются в других потоках и на момент их активации оригинал передаваемого объекта уже успел измениться или даже удалиться.

Обращу внимание, что потоко-безопасна именно Qt-шная реализация, вообще говоря Implicit Sharing не обязан быть потоко-безопасным.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 16, 2011, 20:33
Соль в том что можно передавать по значению и:..
Правильная метода обучения - бить начинающего мокрым веником по заднице за каждую передачу/возврат структуры по значению. А когда научится - разберется с implicit sharing (если захочет). А все эти "вообще нельзя но все-таки можно.." ничего хорошего не приносят.

Да и неясно в плюс тот sharing или в минус - грузить d-> может быть ощутимым, когда-то понадобится STL контейнер и.т.п.

Обращу внимание, что потоко-безопасна именно Qt-шная реализация, вообще говоря Implicit Sharing не обязан быть потоко-безопасным.
Qt реализация безопасна только в том смысле что нитка может делать shared копию данных - и потом ее менять. Но это не значит что 2 или более ниток могут читать/писать одновременно одни и те же данные без синхронизации, поэтому смысла в той безопасности немного.


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 16, 2011, 21:31
Правильная метода обучения - бить начинающего мокрым веником по заднице за каждую передачу/возврат структуры по значению. А когда научится - разберется с implicit sharing (если захочет).
Я где-то говорил, что Qt подходит для обучения азам программирования? :o

Да и неясно в плюс тот sharing или в минус - грузить d-> может быть ощутимым, когда-то понадобится STL контейнер и.т.п.
Всё когда-то может пригодиться.
К слову, в gcc std::string, например, тоже copy-on-write, хотя это конечно не контейнер, но всё же часть STL.

Qt реализация безопасна только в том смысле что нитка может делать shared копию данных - и потом ее менять.
Да, я именно об этом и сказал. Выше указанная std::string нам тут безопасность не гарантирует.

Такая концепция классов родилась в Qt4 прежде всего именно ради безопасной передачи в сигналах/слотах без излишнего копирования.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 16, 2011, 22:47
Да, я именно об этом и сказал. Выше указанная std::string нам тут безопасность не гарантирует.
Ну это легко обходится, давеча изучали  :)

Такая концепция классов родилась в Qt4 прежде всего именно ради безопасной передачи в сигналах/слотах без излишнего копирования.
А в чем эта безопасность, и что выиграли по сравнению с простой передачей по указателю/ссылке? Кому нужна копия - и сам ее сделает (а заодно и подумает есть ли лучшие решения). А изменить оригинал - все равно лочить. Не так уж легко привести пример где implicit дает какой-то выигрыш. А вот "ненужного изучения мнимых тонкостей" он добавляет немало.

Я где-то говорил, что Qt подходит для обучения азам программирования? :o
К сожалению - слишком подходит  :)


Название: Re: Передача по ссылке или указателю?
Отправлено: brankovic от Август 16, 2011, 23:05
Да, я именно об этом и сказал. Выше указанная std::string нам тут безопасность не гарантирует.
Ну это легко обходится, давеча изучали  :)

оффтоп: ссылку дайте на двеча, если не трудно. Или в 2х словах для проспавших, что именно std::string не гарантирует..

Edit: нашёл, почитал. Там про грязный хак с const_cast. А что std::string что-то не гарантирует это LisandreL ужасы какие-то рассказывает, gcc-шный std::string работает нормально.


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 17, 2011, 00:16
А в чем эта безопасность, и что выиграли по сравнению с простой передачей по указателю/ссылке? Кому нужна копия - и сам ее сделает (а заодно и подумает есть ли лучшие решения). А изменить оригинал - все равно лочить. Не так уж легко привести пример где implicit дает какой-то выигрыш.
Цитата: cpp
void SomeClass::loadPixmap( ... )
{
    QPixmap pixmap;
    pixmap.load( path );
    emit pixmapReady( pixmap );
}
Считаем, что картинка у нас потенциально может быть большой.
Принципиально у нас могут быть 4 типа слота (мы пишем модуль, и какие в итоге из этих вариантов будут использоваться понятия не имеем):
1) slot1( QPixmap ) - коннект прямой, объект не изменяется;
2) slot2( QPixmap ) - коннект прямой, объект изменяется для дальнейшего внутреннего использования;
3) slot3( QPixmap ) - коннект через очередь (возможно другой поток), объект не изменяется;
4) slot4( QPixmap ) - коннект через очередь (возможно другой поток), объект изменяется для дальнейшего внутреннего использования.

а) Передавать по ссылке/указателю нельзя - когда вызовутся slot3 и slot4 объект уже будет разрушен.
Можно конечно породить объект динамически, но тогда не ясно, когда мы сможем его безопасно удалить.
Можно, конечно придумать систему обратной связи, но это сильно усложнит код на ровном месте.
Можно использовать умные указатели с подсчётом ссылок... упс, Qt-шные классы практически это за нас и делают.

б) Можно ли обойтись тут простыми классами без «implicit-sharing» и просто передавать по значению?
Безусловно.
Но, если у нас есть слоты типов slot1 и slot3 будет ненужное копирование и повышенный расход памяти, если слотов несколько.
В слотах slot3 и slot4, скорее всего будет ещё одно копирование при помещение в очередь.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 17, 2011, 13:23
LisandreL. Хороший пример, уже смысл сего понятней, с потокобезопасностью я тоже не догнал.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 17, 2011, 13:26
К сожалению - слишком подходит  :)
У меня более 10 лет стажа, к сожалению не очень развивающего. Типа много работы, короткие сроки и т.п.. В Qt дофига тонкостей. Это скорее для средних программеров, которым придется повышать квалификацию при использовании Qt.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 17, 2011, 14:11
Цитата: cpp
void SomeClass::loadPixmap( ... )
{
    QPixmap pixmap;
    pixmap.load( path );
    emit pixmapReady( pixmap );
}
Считаем, что картинка у нас потенциально может быть большой.
Принципиально у нас могут быть 4 типа слота (мы пишем модуль, и какие в итоге из этих вариантов будут использоваться понятия не имеем):
Должен признать что Ваши аргументы убедительны. Но с др. стороны - пример явно "учебный" (в этом нет ничего плохого). Это типично для Qt: если что-то по-быстрому соорудить - все великолепно, а вот если поглубже копнуть - то не очень :) 

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



Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 17, 2011, 14:55
Напр маловероятно что в реальной задаче загрузчик картинок будет столь прост. Как правило он должен проверить не загружена ли уже такая картинка, и только если нет - загрузить и каким-то образом "зарегистрировать" чтобы не грузить ее потом опять. Если даже нет пользователей картинки, часто имеет смысл оставить ее в кэше. И вот если так начать копать, то быстро выяснится что лучше пусть загрузчик отвечает за удаление, а передать/эмиттить ее пользователям лучше по константной ссылке/указателю (вместо баловства с возвратом по значению)
Такой подход применим, если мы целиком пишем программу.
Если мы пишем только модуль/класс и сигналы составляют на внешний интерфейс, то без лишней надобности плохо полагаться на то, что слот, который почистит за нами переданные данные будет и что он будет один.

Ну и хотелось бы услышать прочесть аргументы против такой организации данных, кроме того, что передача по значению не академична.
Считаете, что производительность сильно страдает? Или что-то ещё?


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 17, 2011, 19:58
В данном случае не академичена. Большинству и в голову не приходит передавать аргументу по значению и это уже на подсознательном уровне, а почему в данном случае так, приходится еще хорошо подумать.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 18, 2011, 06:25
Большинству и в голову не приходит передавать аргументу по значению и это уже на подсознательном уровне,
Дело в том что у многих "начитавшихся классов" это совсем не на подсознательном - ну и получается каша/говнокод.

Ну и хотелось бы услышать прочесть аргументы против такой организации данных, кроме того, что передача по значению не академична.
Считаете, что производительность сильно страдает? Или что-то ещё?
Например

Код
C++ (Qt)
void ProcessImage( QImage img )
{
..
uchar * bits = img.bits();
..
}
 
Код
C++ (Qt)
void ProcessContainer( Container <T> src )
{
..
for (Container <T> ::iterator it = src.begin(); it != src.end(); ++it)
..
}
 
Происходит неявное копирование, которое может обойтись весьма недешево. Но компилятор молчит, более того, это работает и не крашит. Заметим что даже в таких простейших случаях ошибка не бросается в глаза, в гораздо более запутанном реальном коде найти такое практически невозможно.

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



Название: Re: Передача по ссылке или указателю?
Отправлено: lit-uriy от Август 18, 2011, 08:02
>>Так подавляющее большинство классов Qt классов - это умный указатель на QИмяКлассаPrivate.
Не верное утверждение.
приватный класс совсем для других целей и умностью он совсем не наделён


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 18, 2011, 08:09
Происходит неявное копирование, которое может обойтись весьма недешево.
Копирование происходит только в том случае, если на данные ссылается более 1 объекта.
Т.е. дорогое копирование произойдёт, только если раньше было присвоение одного объекта другому, а значит без sharing пресловутое дорогое копирование всё равно произошло бы, причём даже если мы использовали бы const uchar * bits = img.bits();.
Да, копирование произойдёт не в том участке кода, что иногда может быть критичным, но это уже довольно частный случай.

lit-uriy, умностью наделён указатель, а не класс. И да, не QИмяКлассаPrivate, а QИмяКлассаData.


Название: Re: Передача по ссылке или указателю?
Отправлено: lenny от Август 18, 2011, 10:14
Происходит неявное копирование, которое может обойтись весьма недешево.
Копирование происходит только в том случае, если на данные ссылается более 1 объекта.
Т.е. дорогое копирование произойдёт, только если раньше было присвоение одного объекта другому, а значит без sharing пресловутое дорогое копирование всё равно произошло бы, причём даже если мы использовали бы const uchar * bits = img.bits();.
Да, копирование произойдёт не в том участке кода, что иногда может быть критичным, но это уже довольно частный случай.

lit-uriy, умностью наделён указатель, а не класс. И да, не QИмяКлассаPrivate, а QИмяКлассаData.
Механизм интересный, полностью скрывает себя от программиста.


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 18, 2011, 10:33
Механизм интересный, полностью скрывает себя от программиста.
Ну как полностью... Возможность «выстрелить себе в ногу» есть всегда: http://www.prog.org.ru/topic_19004_0.html Было бы желание.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 18, 2011, 19:44
Да, копирование произойдёт не в том участке кода, что иногда может быть критичным, но это уже довольно частный случай.
Вы меня прекрасно понимаете  :)
Получая структуру по значению мы не имеем средств проконтролировать происходит ли копирование или нет - ведь механизм полностью скрыт. Смириться с возможным копированием (мол, да это довольно частный случай) - это прекрасно для студентов (и их преподавателей), никто не будет требовать от лабы "производительности". Но уже на 100К данных (примерно) такое приложение перестает работать

Вообще мне кажется все было не так. Собрались креативные пацаны (тогда они еще не были зажравшимися "троллями") и загорелись интересной идеей типа "вот каждый грамотный знает что гонять воздух по значению плохо - а мы докажем что нет!". Ну и вполне доказали. Но это всего лишь один из возможных подходов/идей, со своими плюсами-минусами (на мой взгляд минусы весомее). А сейчас идея (которая когда-то была творческой) вдалбливается механически и без всякого осмысления - становится грустно  :'(

 


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 18, 2011, 19:53
Возможность «выстрелить себе в ногу» есть всегда: http://www.prog.org.ru/topic_19004_0.html Было бы желание.
Вы уже третий раз об этом упоминаете :) А ведь стрелять себе в ногу весьма полезно - значит чему-то научился. Одно дело прочитать о "корове" - мне все было понятно, и совсем другое - столкнуться с ней на практике. "Много знать" и "уметь сделать" - очень разные вещи  :)


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 18, 2011, 21:04
Вы уже третий раз об этом упоминаете :) А ведь стрелять себе в ногу весьма полезно - значит чему-то научился.
Ну посмотреть, как это делают другие тоже иногда полезно.

Не по теме топика, конечно, но расскажу, как недавно подстрелил ногу:
Код
C++ (Qt)
class ThreadBase : protected QThread
{
   Q_OBJECT
public:
   explicit ThreadBase( QObject* = 0 );
   bool isInitialized();
   void launch();
   ~ThreadBase();
 
protected:
   virtual bool initialize();
   virtual void run();
   virtual void execute() = 0;
   virtual void finalize();
   bool initialized;
   bool finish;
};
Код
C++ (Qt)
ThreadBase :: ThreadBase( QObject* parent ) :
   QThread( parent )
{
   initialized = false;
   finish = false;
}
 
ThreadBase :: ~ThreadBase()
{
   finish = true;
   quit();
   while ( isRunning() )
   {
       yieldCurrentThread();
   }
}
 
bool ThreadBase :: isInitialized()
{
   return initialized;
}
 
bool ThreadBase :: initialize()
{
   return true;
}
 
void ThreadBase :: run()
{
   initialized = initialize();
   if ( initialized )
   {
       while( !finish )
       {
           execute();
           usleep( 1 );
       }
   }
   else
   {
       finish = true;
   }
   finalize();
}
 
void ThreadBase :: finalize()
{
}
Дальше я наследую от этого класса и переопределяю initialize, execute и finalize. И вроде бы всё работает, но потом я понимаю, что не всё работает так, как задумывалось.
А теперь вопрос: что было не так и где же я ошибся?

Ответ: Не выполнялась финализация, в итоге и память текла и, что намного хуже, не сохранялись некоторые настройки, не передавались результаты и т.п.
Если унаследованный класс потока удаляется, когда он ещё не вышел из run(), то по выходе из цикла while вызовется finalize() самого ThreadBase, а вовсе не унаследованного класса. Мы все, конечно, знаем, что виртуальные функции из деструктора вызывать нельзя, но то, что в вызванной намного раньше деструктора может вызваться не перегруженная версия функции finalize из-за того, что в v-table уж стёрты данные об унаследованном классе, я не подумал.

Вообще мне кажется все было не так. Собрались креативные пацаны (тогда они еще не были зажравшимися "троллями") и загорелись интересной идеей типа "вот каждый грамотный знает что гонять воздух по значению плохо - а мы докажем что нет!".
Я всё же останусь при мнении, что это было сделано именно для передачи параметров в сигналах/слотах.


Название: Re: Передача по ссылке или указателю?
Отправлено: brankovic от Август 18, 2011, 21:54
Если унаследованный класс потока удаляется, когда он ещё не вышел из run(),

А при чём тут виртуальные функции, разве можно вообще пользоваться объектом после удаления? (если наследник удаляется, то база тоже, т.е. сам QThread тоже уже удалён?)


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 18, 2011, 22:10
А при чём тут виртуальные функции, разве можно вообще пользоваться объектом после удаления? (если наследник удаляется, то база тоже, т.е. сам QThread тоже уже удалён?)
Мы говорим про момент, когда мы находимся в деструкторе ~ThreadBase(). ~QThread() ещё не вызывался, так что к полям этих двух классов можно обращаться, к полям дочернего класса - нет.
P.S. В данном классе всё могло бы сработать - в финализации были только закрытие БД и запись в лог, но в общем случае делать финализацию после того, как первый деструктор отработает действительно дурацкий, так как в общем случае поля объекта могут понадобиться.


Название: Re: Передача по ссылке или указателю?
Отправлено: Igors от Август 19, 2011, 02:41
Не вижу прямой ошибки но дызайн мне не нравится
Код
C++ (Qt)
ThreadBase :: ~ThreadBase()
{
   finish = true;
   quit();
   while ( isRunning() )
   {
       yieldCurrentThread();
   }
}
Деструктор вызовется уже когда нитка завершилась, поэтому quit() и isRunning бесполезны. Если же это задумывалось для "delete this" (из нитки) то while никогда не кончится, да и в любом случае удалять нельзя пока нитка не завершена

Отот finish я бы просто застрелил, ничего хорошего он не делает.
Код
C++ (Qt)
       while (execute())
           usleep( 1 );    // а вместо этого лучше виртуальчик
 



Название: Re: Передача по ссылке или указателю?
Отправлено: brankovic от Август 19, 2011, 11:27
Мы говорим про момент, когда мы находимся в деструкторе ~ThreadBase ()

понял, ~ThreadBase как бы поджидает на выходе, поэтому поля finish и прочие ещё не разрушились :)

Edit: хотя нет, не понятно. Пусть есть тред 1 (главный) и тред 2 (в его контексте выполняется ThreadDerived). Кто вызвал деструктор ThreadDerived? Если тред 1, то он завис на время выполнения деструктора. Если тред 2, то он был в execute и вообще зависнет. Можете пояснить всё-таки кто вызвал деструктор треда?


Название: Re: Передача по ссылке или указателю?
Отправлено: LisandreL от Август 19, 2011, 14:19
Если тред 1, то он завис на время выполнения деструктора. Если тред 2, то он был в execute и вообще зависнет.
Родительский поток. Да зависнет. Эти потоки существуют в течении всего жизненного цикла программы и уничтожаются при её завершении. Так что ничего особо плохого в этом зависании нет.