Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > virtual function


Автор: sergioK1 12.7.2013, 19:55
Собвенно когда вышла жава все были довольны тем что все функции виртуальны ,
тем более что тормоза по этой причине за которые многие ругали OOP полный миф ,
Ну язык он есть такой , ну покему когда сам разрабатывешь не сделать virtual ? 
захотят перегрузить перегрузят , не захотят - ничего не случиться, 

P.S. в С# такая же фича , разрабатывать не возможно  smile 

Автор: mes 12.7.2013, 20:45
sergioK1, а С++ тут при чем ? smile  smile 

Автор: sergioK1 12.7.2013, 21:53
Цитата(mes @ 12.7.2013,  19:45)
sergioK1, а С++ тут при чем ? smile  smile

При том читать надо что пишут  smile 
На С++  разрабатывать тяжело без возможности наследовать  почему не облегчить жизнь, тупо  smile прописав virtual везде, рапить можно но это 1/2 решение контейнер  не примет ,
Если второй раз не понял - меняй профессию  smile  smile 




Автор: mes 12.7.2013, 23:02
Цитата(sergioK1 @  12.7.2013,  20:53 Найти цитируемый пост)
На С++  разрабатывать тяжело без возможности наследовать  почему не облегчить жизнь, тупо

а зачем делать тупо, когда для тех, кому нехватает прелестей явы, могу спокойно перейти на ява  ?

С++ предоставляет гораздо больше возможностей, часть из которых Вы хотите легким движением руки отсечь, только потому, что лень виртуал написать, когда это нужно.. У С++ другие цели и другая идеология...



Автор: sergioK1 12.7.2013, 23:57
Цитата(mes @ 12.7.2013,  22:02)
Цитата(sergioK1 @  12.7.2013,  20:53 Найти цитируемый пост)
На С++  разрабатывать тяжело без возможности наследовать  почему не облегчить жизнь, тупо

а зачем делать тупо, когда для тех, кому нехватает прелестей явы, могу спокойно перейти на ява  ?

С++ предоставляет гораздо больше возможностей, часть из которых Вы хотите легким движением руки отсечь, только потому, что лень виртуал написать, когда это нужно.. У С++ другие цели и другая идеология...


Код

 class  Reader{
  
 public :
  AnyClass getData(){
     ......
.........
}

}



Что отсечь?  наооборот добавить для удобства,
Ясно что getData может быть изменен так чего не поставить virtual ?  
Не та идеология в чем ?  

Автор: feodorv 13.7.2013, 07:57
Помню, в начале-середине 90-х был такой борландовский продукт - TurboVision. Мне понадобилось-то внести небольшие изменения в оболочку одной программы, основанной на этом самом TurboVision. Ха. Нужные методы не были объявлены virtual. Хорошо, что Борланд дал исходные коды TurboVision. Перекомпиляция всего TurboVision на новейшей скоростной 386 системе занимала 4 часа. Путём оптимизации заголовков удалось снизить это время до получаса. Потом ещё несколько раз я вставлял virtual в нужные мне места. И если бы изначально все методы были бы виртуальными, то вся эта дурацкая деятельность была бы не нужна. Поэтому считаю вопрос правомерным.

С другой стороны, так изначально был построен C++. Ну вот именно так и никак иначе. Решение о том, будет какой-либо метод виртуальным, принимается разработчиком исходя из логики проектирования классов. Посмотрите на MFC - большинство методов виртуальные. Но не все! В java я тоже могу понатыкать final где не попадя, осложнив жизнь другим программистам. Шучу))) Поэтому вопрос "когда сам разрабатываешь, не сделать virtual" теряет свою правомерность и отправляется целиком к разработчику конкретного продукта...


Цитата(sergioK1 @  12.7.2013,  20:55 Найти цитируемый пост)
захотят перегрузить перегрузят , не захотят - ничего не случиться

Вот, кстати, не факт.

Автор: sergioK1 13.7.2013, 08:48
Цитата(feodorv @ 13.7.2013,  06:57)
Помню, в начале-середине 90-х был такой борландовский продукт - TurboVision. Мне понадобилось-то внести небольшие изменения в оболочку одной программы, основанной на этом самом TurboVision. Ха. Нужные методы не были объявлены virtual. Хорошо, что Борланд дал исходные коды TurboVision. Перекомпиляция всего TurboVision на новейшей скоростной 386 системе занимала 4 часа. Путём оптимизации заголовков удалось снизить это время до получаса. Потом ещё несколько раз я вставлял virtual в нужные мне места. И если бы изначально все методы были бы виртуальными, то вся эта дурацкая деятельность была бы не нужна. Поэтому считаю вопрос правомерным.

С другой стороны, так изначально был построен C++. Ну вот именно так и никак иначе. Решение о том, будет какой-либо метод виртуальным, принимается разработчиком исходя из логики проектирования классов. Посмотрите на MFC - большинство методов виртуальные. Но не все! В java я тоже могу понатыкать final где не попадя, осложнив жизнь другим программистам. Шучу))) Поэтому вопрос "когда сам разрабатываешь, не сделать virtual" теряет свою правомерность и отправляется целиком к разработчику конкретного продукта...


Цитата(sergioK1 @  12.7.2013,  20:55 Найти цитируемый пост)
захотят перегрузить перегрузят , не захотят - ничего не случиться

Вот, кстати, не факт.

В MFC да , 
В яве ставят final когда осознанно  хотят закрыть наследование осознанно, 
в С++ какие соображения? тот же  буст хоть и студенты писали (хоть и Беркли) ну не могли же они не понимать необходимости в возможной перегрузке например  stack  reserve ,

Автор: mes 13.7.2013, 09:33
Цитата(feodorv @  13.7.2013,  06:57 Найти цитируемый пост)
 И если бы изначально все методы были бы виртуальными, то вся эта дурацкая деятельность была бы не нужна. Поэтому считаю вопрос правомерным.

1. если  автоматом у каждого класса будет  таблица виртуальных методов, то о метапрограммированнии можно будет забыть.. 
2. в моей практиктике витуальная функция одна на сто или более невиртуальных... а в некоторых случаях меня не устраивает родная виртуальность, и я пишу свою..  тоже забыть ??
3. в С++ структура и класс ничем по сути не отличаются...  В блоки данных тоже помещать таблицу ? 
4. Зачастую интерфейс про функцию намного более приемлим, чем интерфейс про класс.. И зачем тогда классовая виртуальность  ? 
5. ну про конструктор умолчим...
и многое многое другое... сейчас лень напрягаться и вспоминать... 

Цитата(sergioK1 @  13.7.2013,  07:48 Найти цитируемый пост)
в С++ какие соображения? тот же  буст хоть и студенты писали (хоть и Беркли) ну не могли же они не понимать необходимости в возможной перегрузке 

перегрузку(overload) с переопределением(override) не путатете ?  что ж тогда на виртуальность то замахнулись ? 
нужна виртуальность во всем берите другие языки, в которых еще и рефлексия к тому же есть.. Нужен еще С++ - используйте связку.. Не надо все разнообразие задач сводить под студенческие требования... 

Автор: sergioK1 13.7.2013, 18:12
Цитата(mes @ 13.7.2013,  08:33)
Цитата(feodorv @  13.7.2013,  06:57 Найти цитируемый пост)
 И если бы изначально все методы были бы виртуальными, то вся эта дурацкая деятельность была бы не нужна. Поэтому считаю вопрос правомерным.

1. если  автоматом у каждого класса будет  таблица виртуальных методов, то о метапрограммированнии можно будет забыть.. 
2. в моей практиктике витуальная функция одна на сто или более невиртуальных... а в некоторых случаях меня не устраивает родная виртуальность, и я пишу свою..  тоже забыть ??
3. в С++ структура и класс ничем по сути не отличаются...  В блоки данных тоже помещать таблицу ? 
4. Зачастую интерфейс про функцию намного более приемлим, чем интерфейс про класс.. И зачем тогда классовая виртуальность  ? 
5. ну про конструктор умолчим...
и многое многое другое... сейчас лень напрягаться и вспоминать... 

Цитата(sergioK1 @  13.7.2013,  07:48 Найти цитируемый пост)
в С++ какие соображения? тот же  буст хоть и студенты писали (хоть и Беркли) ну не могли же они не понимать необходимости в возможной перегрузке 

перегрузку(overload) с переопределением(override) не путатете ?  что ж тогда на виртуальность то замахнулись ? 
нужна виртуальность во всем берите другие языки, в которых еще и рефлексия к тому же есть.. Нужен еще С++ - используйте связку.. Не надо все разнообразие задач сводить под студенческие требования...

Ой только не надо лигвистики  :-D   перегрузка это когда есть функция а наследник имет свою реализацию ,
хотите пусть будет переопределение,

Если честно Я ваших аргуметов ничего не понял , ни про  метопрограмирования  ни про class vs structure .
 в других языках это нормально, в С++ нет какая разница,  ?  По проще можно .



Автор: mes 13.7.2013, 22:29
Цитата(sergioK1 @  13.7.2013,  17:12 Найти цитируемый пост)
перегрузка это когда есть функция а наследник имет свою реализацию ,
хотите пусть будет переопределение,

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


Цитата(sergioK1 @  13.7.2013,  17:12 Найти цитируемый пост)
в других языках это нормально, в С++ нет какая разница

другие языки не предоставляют того, что предоставляет С++.. именно за это многообразие, а также за наличия прямого доступа к внутренним представлениям и ругают многие С++.. мол много учить, или мол легко прострелить себе колено..  Все то оно конечно оно так, но зато какие возможности открывает за собой С++. 
Да многим этим возможности и не нужны, но с другой стороны для них уже дельфи с шарпом есть..

Да С++ не лишен недостатков, и много где он бессилен.. Но на то и разнообразие языков, чтоб для каждой задачи выбирать подходящий...

Автор: volatile 13.7.2013, 22:40
Цитата(sergioK1 @  12.7.2013,  19:55 Найти цитируемый пост)
ну покему когда сам разрабатывешь не сделать virtual ? 

Делайте. Вам что, кто-то здесь запрещает?

sergioK1, Какова цель вашего выступления? Что вы предлагаете?


Автор: mes 13.7.2013, 22:42
P.S. я не против внедрения механизмом, испробованных одним языком в другой.. я против привведения С++ в только-студенто-ориентированный язык... 
вначале посмотрите сколько людей, которые вообще не нужна виртуальность... и за что им будут связывать руки ?!
мало того, что цена вызова дорогая, к тому же лишний размер.. но это мелочи, хоть за нич тоже надо платить..
но вот к примеру, как мне в случае маниакальной виртуальности, получить структуру совместимую с С ?  заводить новый тип для обычных структур ? а зачем ?

Автор: sergioK1 14.7.2013, 06:48
Цитата(volatile @ 13.7.2013,  21:40)

sergioK1, Какова цель вашего выступления? Что вы предлагаете?

Я предлагаю строить грамотный дизайн даже если это  на С++ smile 
И там где есть бизнес обьект он может быть перегружен наследником ,


И еще чтобы mes внятно и по русски  разьяснил третьей попытки ,  что он имеет ввиду  smile ,
 желательно простыми примерами,
вызов виртуал функции дороже чем обычной - чушь  smile 
Даже обсуждать лень,

Автор: mes 14.7.2013, 10:37
Цитата(sergioK1 @  14.7.2013,  05:48 Найти цитируемый пост)
вызов виртуал функции дороже чем обычной - чушь   
Даже обсуждать лень,

скажите это микроконролерщикам.. которым даже обычный вызов бывает дорог.. 



Цитата(sergioK1 @  14.7.2013,  05:48 Найти цитируемый пост)
И еще чтобы mes внятно и по русски  разьяснил третьей попытки ,  что он имеет ввиду   ,
 желательно простыми примерами,


Цитата(sergioK1 @  14.7.2013,  05:48 Найти цитируемый пост)
Я предлагаю строить грамотный дизайн даже если это  на С++  

так я двумя руками за граммотный.. только я против считать явскую корявость граммотным дизайном..  Пусть на ней студенты развлекаются... 


http://codepad.org/kGYnfiS8
Код

#include <iostream>

#define V

struct proxy
{
  V bool is_special1() const { return _ch == 255; }
  V bool is_special2() const { return _ch == 254; }
  V bool is_null() const { return _ch == 0;  }

private:
  unsigned char _ch;

};

std::ostream& operator <<(std::ostream& os, proxy const& ch)
{
 os
 << (ch.is_special1() ? "special1" : 
     ch.is_special2() ? "special2" :
     ch.is_null() ? "null" : "other");

 return os;
}

unsigned char data[] = { 10,255,30,23,254,0,40 };
const size_t data_size = sizeof(data)/sizeof(*data);

int main()
{
  for(size_t i=0; i<data_size; ++i)
    std::cout << int(data[i]) << " is " << (*reinterpret_cast<proxy*>(data+i)) << std::endl;

}

теперь попробуйте повторить с #define V virtual.. 

Автор: volatile 14.7.2013, 10:56
Цитата(sergioK1 @  14.7.2013,  06:48 Найти цитируемый пост)
вызов виртуал функции дороже чем обычной - чушь   
Даже обсуждать лень, 

Этот вопрос (слава богу!) не нужно обсуждать. Достаточно просто измерить:
Код

Times for:
virtual function = 70.0,  plain function = 10.0
virtual function = 80.0,  plain function = 10.0
virtual function = 90.0,  plain function = 10.0

http://codepad.org/J9Vb0nl9
На домашней машине, при полной оптимизации, виртуальная функция в среднем в 5 раз медленее чем простая.

Автор: sergioK1 14.7.2013, 21:21
Цитата(volatile @ 14.7.2013,  09:56)
Цитата(sergioK1 @  14.7.2013,  06:48 Найти цитируемый пост)
вызов виртуал функции дороже чем обычной - чушь   
Даже обсуждать лень, 

Этот вопрос (слава богу!) не нужно обсуждать. Достаточно просто измерить:
Код

Times for:
virtual function = 70.0,  plain function = 10.0
virtual function = 80.0,  plain function = 10.0
virtual function = 90.0,  plain function = 10.0

http://codepad.org/J9Vb0nl9
На домашней машине, при полной оптимизации, виртуальная функция в среднем в 5 раз медленее чем простая.

угу на вебе  smile  и в 10 раз быстрее ,  там девация 0 вас не это смущает, мне кажеться что 
там интерпритатор , который имулирует компиляцию 
Я измерил открыл Code Block c gcc хотя надо бы в студии но ее счас нетy,
Код

Times for:
virtual function = 173.0,  plain function = 157.0
virtual function = 162.0,  plain function = 152.0
virtual function = 167.0,  plain function = 165.0

sum = 1863248640

Process returned 0 (0x0)   execution time : 1.005 s
Press any key to continue.



проделал раз 10 максимум что удалось получить это 192,0 vs 158.0

на 11раз получил 

Times for:
virtual function = 172.0,  plain function = 182.0
virtual function = 158.0,  plain function = 147.0
virtual function = 160.0,  plain function = 150.0

sum = 1863248640

Process returned 0 (0x0)   execution time : 0.995 s
Press any key to continue.


потом проверил еще пару раз и вот 
Times for:
virtual function = 177.0,  plain function = 161.0
virtual function = 189.0,  plain function = 205.0

virtual function = 170.0,  plain function = 162.0

sum = 1863248640

Process returned 0 (0x0)   execution time : 1.091 s
Press any key to continue

т,е мы видим что разницы нет , 


mes ,
сказал  ответили что ставять inline smile  это раз и пишут на С это два ,
вызов VF в сpp это сишный вызов поинтера на функцию ,  хотя там где critical necessary компайлеру лучше не доверяться а делать самому, классический пример "умная пуля" , 

Я говорил про бизнес  обьекты, с "тупо везде" перегнул,  у вас скорее конвертер который вообще должен быть static ,
IMHO,




Автор: NoviceF 14.7.2013, 21:42
не знаю, где в креаторе наболтать оптимизацию, но по дефолту на нетбуке цифры такие

Times for:
virtual function = 300.0,  plain function = 50.0
virtual function = 300.0,  plain function = 50.0
virtual function = 300.0,  plain function = 50.0


sergioK1, релиз запусти, а не дебаг.

Автор: sergioK1 14.7.2013, 22:38
Цитата(NoviceF @ 14.7.2013,  20:42)
не знаю, где в креаторе наболтать оптимизацию, но по дефолту на нетбуке цифры такие

Times for:
virtual function = 300.0,  plain function = 50.0
virtual function = 300.0,  plain function = 50.0
virtual function = 300.0,  plain function = 50.0


sergioK1, релиз запусти, а не дебаг.

Да в дебаге без разницы в релизе в 4раза
И не помогает оптимизация , Страустроп пишет что разница  не значительная  ,
он не знает С++ ??? 

Автор: bsa 14.7.2013, 22:46
sergioK1, что такое вызвать функцию? Это делает инструкция call <address>. Она есть практически в любом процессоре. Но в некоторых РИСКах ее может не быть, тогда это делается вручную: адрес возврата записывается в стек, указатель стека смещается на размер адреса возврата, происходит загрузка в указатель команд адреса процедуры.
Когда имеется вызов виртуальной функции, то тут все сложнее: сохранить адрес возврата, сместить указатель вершины стека, загрузить адрес процедуры в указатель команд, загрузить адрес таблицы виртуальных функций, добавить смещение, загрузить адрес функции.
А теперь вспомним, что на С++ пишут в основном не под микроконтроллеры, а под "большие" машины архитектуры x86 (или x86_64). Чем она отличается? А тем, что кроме всего вышеперечисленного есть кэш и многостадийный конвеер. Таким образом, если производится одновременная работа с большим количеством данных, то кэш начинает тормозить весь процесс - происходят постоянно промахи и приходится заменять одни данные другими. А вот конвеер спотыкается на переходах. И, есть у меня подозрение, что их предсказатель далеко не всегда успешно справляется при вызове виртуальных функций, так как в момент принятия решения адрес перехода может фактически отсутствовать в доступности процессора. А если предсказание перехода завершилось неудачей, то после выявления этого весь конвеер будет сброшен и процессор "потеряет" более десятка тактов...

Когда разработчик проектирует класс, он предусматривает, что пользователь может переопределить, а что в принципе не должен. Причем, опыт показывает, что 90% методов классов в переопределении не нуждаются.

Главная философия языка "не делать то, о чем не просили".

Если ты хочешь переопределить у стека метод reserve (нахрена?), то сделай это путем агрегации (сделай свой стек, который содержит в себе стандартный). Все равно, все члены класса приватные и ты из потомка к ним доступа не получишь.
Если ты хочешь изменить аллокатор, то может стоит именно это и сделать? Посмотри на второй параметр шаблона.

И не забывай, что все стандартные контейнеры самодостаточны. Их не требуется как-то "дореализовывать".

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

Автор: sergioK1 14.7.2013, 23:46
volatile 
NoviceF

Это обман трудящихся   smile  разбейте все по отдельным файлам , 
у меня даже 1% разницы нет , 

Автор: CosmoMan 29.7.2013, 11:14
Я одно время тестировал оверхед при использовании виртуальных функций в классе. 
Это очень актуально для геймдева, где может каждый фрейм перебираться миллион объектов в цикле и дергаться у них виртуальные методы.
Особенно если объекты многокомпонентные (много стратегий поведения с общим интерфейсом).
там это имеет значение и может существенно влиять на производительность. 

Я считаю, что делать все функции виртуальными - это лишнее.
Но думаю не помешал бы блок вроде public virtual:

Автор: volatile 29.7.2013, 13:08
Цитата(sergioK1 @  14.7.2013,  23:46 Найти цитируемый пост)
Это обман трудящихся     разбейте все по отдельным файлам , 
у меня даже 1% разницы нет , 

sergioK1,  тут уже говорилось что код нужно проверять на максимальной оптимизации на скорость.
То что у вас скорость зависит от "разбиения на файлы", говорит лишь о том что у вас не включен элементаный
link-time code generation.

Да и потом изучайте мат-часть.
То что вирт. функция будет медленней - даже спорить смешно.

Автор: sergioK1 29.7.2013, 13:28
Цитата(volatile @ 29.7.2013,  12:08)
То что у вас скорость зависит от "разбиения на файлы", говорит лишь о том что у вас не включен элементаный
link-time code generation.

Да и потом изучайте мат-часть.
То что вирт. функция будет медленней - даже спорить смешно.

где его включать? 

вопрос неправильно  ставите(это масчеи матчасти  smile )  не virtual vs regular , 
а static call vs dynamic call , т,е вопрос можно ли оптимизировать что-бы был статик?
в чистом С это можно(судя по тестам), почему в С++ нет ? 


Автор: volatile 29.7.2013, 14:01
Цитата(sergioK1 @  29.7.2013,  13:28 Найти цитируемый пост)
можно ли оптимизировать что-бы был статик?
в чистом С это можно(судя по тестам), почему в С++ нет ? 

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

В случае не вирт. функции, никакого вычисления в ран-тайме НЕ нужно.
Все известно на момент компиляции. И компилятор просто подставляет жесткий адрес функции.

Автор: mes 29.7.2013, 15:38
Цитата(volatile @  29.7.2013,  13:01 Найти цитируемый пост)
Нигде нельзя. 

если известен тип, с++ позволяет оптимизировать виртуальный вызов..

Автор: volatile 29.7.2013, 15:50
Цитата(mes @  29.7.2013,  15:38 Найти цитируемый пост)
если известен тип, с++ позволяет оптимизировать виртуальный вызов.. 

Если известен тип, тогда смысла в виртуальной функции нет.

Добавлено через 2 минуты и 37 секунд
Естествеено, если взять пример с одним типом, как код выше, ясно что там другого просто не будет.
В реальных программах (сколько нибудь сложных) - так не бывает.


Автор: mes 29.7.2013, 16:03
Цитата(volatile @  29.7.2013,  14:50 Найти цитируемый пост)
Если известен тип, тогда смысла в виртуальной функции нет.

зато есть смысл в оптимизации такого вызова smile


Цитата(volatile @  29.7.2013,  14:50 Найти цитируемый пост)
Естествеено, если взять пример с одним типом, как код выше, ясно что там другого просто не будет.
В реальных программах (сколько нибудь сложных) - так не бывает.

зато бывает в тестах, что мешает некоторым (как например тс)  адекватно оценить затраты на виртуальность..

Добавлено через 2 минуты и 23 секунды
Цитата(volatile @  29.7.2013,  14:50 Найти цитируемый пост)
В реальных программах (сколько нибудь сложных) - так не бывает.

и все ж бывает..

Автор: volatile 29.7.2013, 16:29
Аналогично, можно еще очень сильно настаивать на том, что скорость умножения, вообще не имеет значения в программировании
основываясь на том что высосанный из пальца тестовый код  выполняется моментально:
Код

а = 2 * 2;


Да здесь не будет умножения в ран-тайме, но это вовсе не означает что можно теперь плевать на скорость умножения.
Да бывает что так пишут. Но это капля в море. и вообще не о том речь.

Добавлено через 3 минуты и 54 секунды

Так - же и с вашими оптимизациями вирт.функций.
Там где их компилятор сможет оптимизировать, там вообще не нужно виртализация.
И лучше рассмотеть использвание шаблонов!

Там гораздо надежнее получите высокооптимизированный код

Автор: mes 29.7.2013, 18:27
Цитата(volatile @  29.7.2013,  15:29 Найти цитируемый пост)
Так - же и с вашими оптимизациями вирт.функций.
Там где их компилятор сможет оптимизировать, там вообще не нужно виртализация.
И лучше рассмотеть использвание шаблонов!

как по мне тут соленное с влажным сравнивается.. 

Автор: volatile 29.7.2013, 20:37
Цитата(mes @  29.7.2013,  18:27 Найти цитируемый пост)
как по мне тут соленное с влажным сравнивается..  

Вот здесь люди тоже сравнивают :
http://stackoverflow.com/questions/9907004/dyamic-vs-static-polymorphism-in-c-which-is-preferable
есть и еще с десяток тысяч линков, найдете без труда в гугле...

Автор: mes 29.7.2013, 21:48
Цитата(volatile @  29.7.2013,  19:37 Найти цитируемый пост)
Сравнение влажного с соленым

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

Автор: borisbn 29.7.2013, 22:15
Цитата(bsa @  14.7.2013,  22:46 Найти цитируемый пост)
все стандартные контейнеры самодостаточны. Их не требуется как-то "дореализовывать".

Как говорит мОлодежь в интернетах - я бы вд..... дореализовывал...
Вот смотрите: такая комбинация в пользовательском коде:
Код
int x = container.back();
container.pop_back();
...

мягко говоря - не редкость. Так почему бы не реализовать
Код
T Container::takeBack();

как в той же КуТе ?

Или такой пользовательский код:
Код
std::vector< int >::const_iterator found = v.find( x );
if ( found != v.end() ) {
    ...
};

есть у 146% программистов. Так почему не ввести в контейнер функцию bool contains( T x ) const; ?

Я уже молчу про убогийший http://en.cppreference.com/w/cpp/string/basic_string/replace... Ну, везде... Просто везде... есть функция замены подстроки в строке на другую строку. Типа
Код
replace( string subStringToFind, string subStringToReplaceWith, Enum replaceAllOrReplaceFirst = ReplaceAll );


По существу вопроса: ИМХО, если ты достиг такого уровня в программировании, что ускорить твою программу может только (!!!) переход от виртуальных функций к обычным, то тебе нужно не задавать вопросы на форумах, а как минимум отвечать на них, а как максимум писать соответствующие статьи

P.S. Надо бы для "Premature optimization is the root of all evil" придумать такую же известную и запоминающуюся аббревиатуру как IMHO. Пусть будет PORE  smile 

Автор: volatile 29.7.2013, 22:47
Цитата(mes @  29.7.2013,  21:48 Найти цитируемый пост)
я не о сравнение динамического и статического полиморфизма,
а о сравнение оптимизации виртуального вызова со статическим полиморфизмом...

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

Вообще, имхо, начинаете жонгллировать словами... 


Цитата(borisbn @  29.7.2013,  22:15 Найти цитируемый пост)
Надо бы для "Premature optimization is the root of all evil"

borisbn, по-сути да. В большинстве случаев не нужно предельное быстродействие. И можно пренебречь скоростью.

Просто здесь пытаются убедить что виртуалиация не медленее чем простой вызов.
А это в принципе не верно.


Автор: sergioK1 30.7.2013, 10:48
Цитата(mes @ 29.7.2013,  15:03)
зато бывает в тестах, что мешает некоторым (как например тс)  адекватно оценить затраты на виртуальность..

Ну покажите свой пример, который не мешает реальной оценке затрат, 


borisbn
То что можно испортить стандартный код всем известно , 
Элементарный thead safety как ты сделаешь ?
Eсть у тебя foo(Vector& v ) - т,е рапер не пройдет 
и std::set где равные элементы это те где е1-е2 < некой дельты ,
опять же не меняя интерфейса 
foo(std::set& s) .

только других либ не предлагай  smile 

Тот кто скажет что такого в реальной жизни не бывает , как volatile
 просто не пишет больших апликаций  smile 
 



Автор: bsa 30.7.2013, 11:47
Цитата(borisbn @  29.7.2013,  23:15 Найти цитируемый пост)
int x = container.back();
container.pop_back();
О да! Нужная вещь... Вот только есть одна маленькая проблема, несущественная для простых типов. В случае takeBack произойдет 100% копирование объекта (в Qt это решено тотальным использованием PIMPL и COW). И если у тебя та же строка, то займет это не мало времени (хотя, в C++11 это можно эффективно оптимизировать).

Цитата(borisbn @  29.7.2013,  23:15 Найти цитируемый пост)
std::vector< int >::const_iterator found = v.find( x );
if ( found != v.end() ) {
    ...
};
А вот тут сложней. Я очень редко использую find для вектора (точнее, почти не использую). Так как у меня или сортированные вектора (lower_bound) или множества/мэпы. И у меня код обычно выгдялит так:
Код
container::iterator it = v.find(x);
if (it != v.end()) {
   //что-то делается с *it
}


Я бы вообще все эти штуки делал бы через отдельные функции:
Код
template<class Container, typename ValueType>
bool contains(const Container &c, const ValueType &v)
{
   Container::const_iterator i = c.find(v);
   return i != v.end();
}

Автор: borisbn 30.7.2013, 11:54
Цитата(bsa @  30.7.2013,  11:47 Найти цитируемый пост)
И у меня код обычно выгдялит так

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

Автор: sergioK1 30.7.2013, 12:18
Цитата(bsa @ 30.7.2013,  10:47)
container.back();
container.pop_back();
О да! Нужная вещь... Вот только есть одна маленькая проблема, несущественная для простых типов. В случае takeBack произойдет 100% копирование объекта (в Qt это решено тотальным использованием PIMPL и COW). И если у тебя та же строка, то займет это не мало времени (хотя, в C++11 это можно эффективно оптимизировать).
[/QUOTE]
Когда возникают подобные ситуации то скоростью , размером приходиться жертвовать,
Точнее примемять там где она не критична ,

ну так кто-нидь предложит решение , 

Автор: volatile 30.7.2013, 12:32
Цитата(sergioK1 @  30.7.2013,  10:48 Найти цитируемый пост)
Тот кто скажет что такого в реальной жизни не бывает , как volatile
 

Во первых я это говорил совсем по другому поводу...
Во вторых не нужно придираться к словам.

Вы пытаетесь доказать что умножение не требует затрат, осовываясь на том что
Цитата(volatile @  29.7.2013,  16:29 Найти цитируемый пост)
а = 2 * 2;

вычисляется моментально.

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

Замените слово "умножение", на "виртуализацию".
Все.

Добавлено через 7 минут и 35 секунд
Цитата(sergioK1 @  30.7.2013,  10:48 Найти цитируемый пост)
покажите свой пример, который не мешает реальной оценке затрат, 

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

Автор: volatile 30.7.2013, 13:03
Вы, имхо, вообще измеряте не виртуализацию.  smile 
А я не телепат.что там у вас...

А вообще пишите как хотите.
меня удивляет что вродебы не новичкам, нужно доказывать что белое это белое. а черное это черное.
 smile 

Автор: volatile 30.7.2013, 13:25
http://www.google.com/search?hl=ru&q=Virtual+functions+performance+C%2B%2B&lr=&aq=f&oq=

Автор: sergioK1 30.7.2013, 15:44
Цитата(volatile @ 30.7.2013,  12:03)
меня удивляет что вродебы не новичкам, нужно доказывать что белое это белое. а черное это черное.
 smile


Cтрауспу поверил на слово он пишет что разницы  нет ,  или перевод  sucks .
основная причина  в инлайне IMHO 
Так что белое и что черное ?

Автор: volatile 30.7.2013, 17:45
Цитата(sergioK1 @  30.7.2013,  15:44 Найти цитируемый пост)
Cтрауспу поверил на слово он пишет что разницы  нет 

Где он это пишет? ссыллку дайте.

Автор: sergioK1 30.7.2013, 19:01
Цитата(volatile @ 30.7.2013,  16:45)
Цитата(sergioK1 @  30.7.2013,  15:44 Найти цитируемый пост)
Cтрауспу поверил на слово он пишет что разницы  нет 

Где он это пишет? ссыллку дайте.

В книге страница 515-520 ,

Автор: volatile 30.7.2013, 20:49
Цитата(sergioK1 @  30.7.2013,  19:01 Найти цитируемый пост)
В книге страница 515-520 , 

У вас бред, вам это приснилось ...

Цитата
Q:Why are member functions not virtual by default?
A:Because many classes are not designed to be used as base classes. For example, see class complex. 
Also, objects of a class with a virtual function require space needed by the virtual function call mechanism - typically one word per object. This overhead can be significant, and can get in the way of layout compatibility with data from other languages (e.g. C and Fortran).

http://www.stroustrup.com/bs_faq2.html

Автор: Alexeis 30.7.2013, 21:57
  Все эти оверхеды существенны для всякого рода железок. Для писшника 4 байта VMT ерунда, а вызов функции усложняется на одну инструкцию получения адреса функции по фиксированному смещению от начала VMT. Если для функции такая операция критична по времени, то следует подумать о том чтобы ее заинлайнить. Насколько я помню любая функция из секции импорта также требует дополнительной операции получения адреса, поскольку компилятор лишь знает где будет располагаться адрес функции после загрузки Dll . И никого не волнуют такие затраты. Мне кажется главная причина того что не все функции виртуальные это потеря прозрачности. Виртуальная функция сама по себе "сюрприз". Мы перекрываем виртуальную функцию, чтобы изменить поведение уже написанного кода, однако если код писал другой человек, то мы можем не знать заложенной логики и вместо правильно работающей чужой функции, в некотором месте может вызываться наша, которая уже может не знать замысла автора класса предка. 
  Иногда нужно создать функцию с тем  же именем что и виртуальная, но для своих целей, при этом мы хотим, чтобы замысел автора класса остался неизменным. Она будет заменять функцию предка, в новом классе при этом предок продолжить вызывать свою стабильную функцию. При глубокой иерархии мы можем и не знать имен всех функций и случайно назвать свою функцию также как одна из виртуальных функций предка. 
  Иными словами, виртуальность это некоторый механизм, который нужен только там где он нужен, как например механизм констант. Константы в С++ это круто это гибко, это безопасно, но никому не придет в голову делать все данные константами. 

Автор: bsa 30.7.2013, 22:41
Alexeis, золотые слова

Автор: xvr 6.8.2013, 12:33
Цитата(sergioK1 @  12.7.2013,  19:55 Найти цитируемый пост)
тем более что тормоза по этой причине за которые многие ругали OOP полный миф ,

Гы  smile 
Был случай - одна программа на Java была ускоренна в несколько раз простым прописыванием final ко всем методам всех классов, которые реально не были виртуальными.

Автор: sergioK1 6.8.2013, 13:29
Цитата(xvr @ 6.8.2013,  11:33)
Цитата(sergioK1 @  12.7.2013,  19:55 Найти цитируемый пост)
тем более что тормоза по этой причине за которые многие ругали OOP полный миф ,

Гы  smile 
Был случай - одна программа на Java была ускоренна в несколько раз простым прописыванием final ко всем методам всех классов, которые реально не были виртуальными.


С потоками ? 
Какая  java ? версия ? Желательно код сюда ,
А то  мой телепат в отпуске  smile 
Хотя final или static   в жаве  грузиться заранее это фактически аналог inline ,





Автор: xvr 6.8.2013, 13:37
Цитата(sergioK1 @  6.8.2013,  13:29 Найти цитируемый пост)
И при чем тут виртуальность ?

Это отключение виртуальности. Все остальное - это уже последствия.

Автор: Alexeis 6.8.2013, 14:42
Цитата(xvr @  6.8.2013,  14:37 Найти цитируемый пост)
Это отключение виртуальности. Все остальное - это уже последствия.

  Это отключение корявой реализации чего-то вместе с виртуальностью. Сами же виртуальный вызов в С++ почти ничего не стоит. Есть правда еще один минус виртуальности. Компилятор не может выкидывать функции, которые ни где не использовались, если они виртуальные. Зато может инлайнить, если на 100% уверен, что будет вызвана именно эта функция.

Автор: bsa 6.8.2013, 14:50
Цитата(Alexeis @  6.8.2013,  15:42 Найти цитируемый пост)
Сами же виртуальный вызов в С++ почти ничего не стоит.
смотря по сравнению с чем. Если сравнивать с простым вызовом подпрограммы, то цена огромна, если с библиотечным вызовом, то сопоставима, если с системным - то пренебрежимо мала.
Касается современного "настольного" процессора с длинным конвейером и предсказателем переходов

Автор: sergioK1 6.8.2013, 15:58
Цитата(bsa @ 6.8.2013,  13:50)
Цитата(Alexeis @  6.8.2013,  15:42 Найти цитируемый пост)
Сами же виртуальный вызов в С++ почти ничего не стоит.
смотря по сравнению с чем. Если сравнивать с простым вызовом подпрограммы, то цена огромна, если с библиотечным вызовом, то сопоставима, если с системным - то пренебрежимо мала.
Касается современного "настольного" процессора с длинным конвейером и предсказателем переходов

пример показывай где огромна,  хватит  уже теорий  smile 


Alexeis ,
это установка флага const в поинтер на функцию ,

Автор: Alexeis 6.8.2013, 16:12
Цитата(bsa @  6.8.2013,  15:50 Найти цитируемый пост)
Если сравнивать с простым вызовом подпрограммы

  С этого момента по подробнее. Я где-то раньше читал, что вызов подпрограммы занимает 12 тактов процессора (x86), добавим сюда типичную операцию помещения в стек 1-2х параметров (хз сколько тактов), против дополнительных расходов по извлечению адреса расположенного по фиксированному смещению. По сути 2е операции с памятью. 1я получение адреса VMT, 2я получение адреса функции. Что же здесь такого накладного? Неужели промахи кеша процессора приводят к таким огромным потерям производительности?

Автор: bsa 6.8.2013, 18:02
Alexeis, я же написал, смотря с чем сравнивать. Если ты выполняешь какие-то внутренние вычисления и при этом активно используешь виртуальные методы, то производительности ты потеряешь значительную долю. А вот если 90% времени программа "отдыхает" (что-то делает система), то скорее всего, виртуальность вреда существенного не наносит.

Автор: mes 6.8.2013, 18:34
Цитата(Alexeis @  6.8.2013,  13:42 Найти цитируемый пост)
 Сами же виртуальный вызов в С++ почти ничего не стоит. 

Цитата(Alexeis @  6.8.2013,  13:42 Найти цитируемый пост)
 Компилятор не может выкидывать функции, которые ни где не использовались, если они виртуальные.

а значит все таки стоят smile

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

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

Автор: xvr 6.8.2013, 22:45
Цитата(sergioK1 @  6.8.2013,  15:58 Найти цитируемый пост)
пример показывай где огромна,  хватит  уже теорий

Не так страшен вызов виртуальной функции сам по себе, как то, что напрочь отбивает все возможности компилятора по оптимизации через границу этого вызова. И это касается не только инлайна, но и pointer анализа и вынесения общих подвыражений.
Пример -
Код

class Virtual {
public:
 double some(double v) {return v+1;}
};

double a,b;

double func(Virtual& c)
{
 double acc=0;
 for(int i=0;i<10000000;++i)
  acc+=c.some(pow(a,b));
 return acc;
}
Оптимизатор выкинет все тело цикла, и вернет сразу 10000000*(1+pow(a,b)). Если же функция Virtual::some будет виртуальной - он не сможет сделать ничего - цикл будет крутится 10000000 раз, и pow() вызываться каждый раз

Автор: sergioK1 6.8.2013, 23:38
Цитата(xvr @ 6.8.2013,  21:45)
Цитата(sergioK1 @  6.8.2013,  15:58 Найти цитируемый пост)
пример показывай где огромна,  хватит  уже теорий

Не так страшен вызов виртуальной функции сам по себе, как то, что напрочь отбивает все возможности компилятора по оптимизации через границу этого вызова. И это касается не только инлайна, но и pointer анализа и вынесения общих подвыражений.
Пример -
Код

class Virtual {
public:
 double some(double v) {return v+1;}
};

double a,b;

double func(Virtual& c)
{
 double acc=0;
 for(int i=0;i<10000000;++i)
  acc+=c.some(pow(a,b));
 return acc;
}
Оптимизатор выкинет все тело цикла, и вернет сразу 10000000*(1+pow(a,b)). Если же функция Virtual::some будет виртуальной - он не сможет сделать ничего - цикл будет крутится 10000000 раз, и pow() вызываться каждый раз

Все это так но,
но при нормальном код ревью вы скорее всего оптимизируете алгоритм сами , по крайне мере постараетесь 
это сделать если пример будет более сложный,  компилятор не всемогущ и гарантий 100% что он оптимизирует нет, или нааборот из-за каких то других причин будет нельзя пользоваться оптимизацией .
Т,е ,  "проблемв  тормозов" остаюеться в теории, по крайне мере в основном 

В  java проверил final эффект нулевой 

Автор: xvr 6.8.2013, 23:51
Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
но при нормальном код ревью вы скорее всего оптимизируете алгоритм сами , по крайне мере постараетесь 

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

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
это сделать если пример будет более сложный,  компилятор не всемогущ и гарантий 100% что он оптимизирует нет

Разумеется, но 100% от него никто и не требует. Иначе не было бы инженеров по performance анализу (а они есть, и немало). Однако, ускорение программы в разы при включении оптимизации вполне рядовая вещь.

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
Т,е ,  "проблемв  тормозов" остаюеться в теории, по крайне мере в основном 

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

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
В  java проверил final эффект нулевой  

Я не говорил, что это проявится на любой java программе. Я говорил, что такие программы существуют.

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


Автор: sergioK1 7.8.2013, 10:16
Цитата(xvr @ 6.8.2013,  22:51)
Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
но при нормальном код ревью вы скорее всего оптимизируете алгоритм сами , по крайне мере постараетесь 

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

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
это сделать если пример будет более сложный,  компилятор не всемогущ и гарантий 100% что он оптимизирует нет

Разумеется, но 100% от него никто и не требует. Иначе не было бы инженеров по performance анализу (а они есть, и немало). Однако, ускорение программы в разы при включении оптимизации вполне рядовая вещь.

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
Т,е ,  "проблемв  тормозов" остаюеться в теории, по крайне мере в основном 

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

Цитата(sergioK1 @  6.8.2013,  23:38 Найти цитируемый пост)
В  java проверил final эффект нулевой  

Я не говорил, что это проявится на любой java программе. Я говорил, что такие программы существуют.

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

Да вобщем то про недостатки всех функций  быть виртуальными ,по принципу а может пригодиться ,Я давно понял , надо было раньше об этом произнести  в слух smile 

Про final Я слышал пару раз в реальности ни разу не видел. 


И еше у меня сложилось ощущение в Java не все функции виртуальны , т,е компайлер "умнее" чем в С++ ,
т,е там где наследника нет нету виртуального механизма , Но поскольку документации по теме мизер, то 
остаеться только догадаваться так это или нет ,




Автор: xvr 7.8.2013, 13:04
Цитата(sergioK1 @  7.8.2013,  10:16 Найти цитируемый пост)
И еше у меня сложилось ощущение в Java не все функции виртуальны , т,е компайлер "умнее" чем в С++ ,
т,е там где наследника нет нету виртуального механизма ,

Возможно. Та история, которую я тут упоминал, произошла более 20 лет назад, с тех пор компиляторы сильно поумнели  smile 

Хотя в Java есть 2 вещи, одна из которых сильно мешает а вторая помогает проводить такие оптимизации.

В Java есть динамическая загрузка классов, так что не-виртуальный метод может в run-time привратится в виртуальный, что явно не способствует проведению оптимизации в compile-time  smile 

С другой стороны в Java есть JIT компилятор, так что при таком 'внезапном' изменении статуса виртуальности функции ее можно будет и перекомпилировать.  smile 

Что, увы, автоматически перености большую часть оптимизатора в run-time, что отнюдь не добавляет надежности и устойчивости всей Java системе в целом  smile 

Автор: Amp 7.8.2013, 16:09
У самого Java компилятора маневров для оптимизации кода вызова виртуальных методов не так и много - инструкции invokevirtual и invokeinterface. Использование invokeinterface может сказаться на времени вызова метода не самым лучшим образом. Остальные оптимизации уже за JIT-ом.

Автор: NoviceF 8.8.2013, 13:30
По поводу виртуальный функций есть ещё одно не совсем очевидное следствие, которое не определено стандартом, но зачастую компиляторы поступают именно так.

Когда инстанцируется шаблонный класс, в котором есть виртаульная функция, она будет инстанцирована всегда, даже если в коде она не используется (актуально для старых версий gcc и MSVС компилятора, с новыми не проверял), в отличие от всех других функций шаблонного класса. Что может приводить к невозможности использовать шаблонный класс там, где ограничений для его применения на самом деле нет. Например, невозможно создать экземпляр MFC контейнера CArray с типом с закрытым конструктором по умолчанию из-за виртуальной функции Serialize.

Автор: Alexeis 8.8.2013, 14:52
NoviceF, ну это скорее ограничения обычного класса чем шаблонного. Таблица VMT должна содержать записи всех виртуальных функций и все указатели должны указывать на реальные адреса. Представь, что я каким-нить неявным для компилятора образом образом, получу указатель на предка (к примеру COM интерфейс) и передам его за границу модуля в Dll, и оттуда вызову виртуальную функцию. Раз компилятор не может отследить, то обязан всегда все виртуальные функции помещать в исполняемый модуль и все записи помещать в VMT. 
  В случае же простой функции компилятору достаточно убедиться, что ни где нет оператора взятия адреса функции и она ни где явно не вызывается, чтобы не компилировать ее и не включать в образ. Для виртуальной функции именно неявный вызов является причиной того, что она всегда будет включаться в образ и это не зависит от версии компилятора и не может быть иначе реализовано. 

Автор: Jeka178RUS 13.8.2013, 13:59
Цитата(mes @ 14.7.2013,  10:37)
Код

#include <iostream>

#define V

struct proxy
{
  V bool is_special1() const { return _ch == 255; }
  V bool is_special2() const { return _ch == 254; }
  V bool is_null() const { return _ch == 0;  }

private:
  unsigned char _ch;

};

std::ostream& operator <<(std::ostream& os, proxy const& ch)
{
 os
 << (ch.is_special1() ? "special1" : 
     ch.is_special2() ? "special2" :
     ch.is_null() ? "null" : "other");

 return os;
}

unsigned char data[] = { 10,255,30,23,254,0,40 };
const size_t data_size = sizeof(data)/sizeof(*data);

int main()
{
  for(size_t i=0; i<data_size; ++i)
    std::cout << int(data[i]) << " is " << (*reinterpret_cast<proxy*>(data+i)) << std::endl;

}

теперь попробуйте повторить с #define V virtual..

Ну и жесть smile жутко надуманный пример однако

Автор: mes 16.8.2013, 09:41
Цитата(Jeka178RUS @  13.8.2013,  12:59 Найти цитируемый пост)
Ну и жесть  жутко надуманный пример однако 

нуттак smile , главное он затрагивает нужные аспекты:)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)