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


Автор: xTr1m 26.7.2012, 17:34
Имеем такую функцию. От каждого элемента получить дочерний.

Код

void CSomeClass::GetChildItems( const vector<Item*> &allItems, vector<Item*> &childItems)
{
   for(unsigned int i=0; i<allItems.size(); ++i)
   {
        Item *childItem = allItems[i].GetChild();
        if(childItem != 0)
            childItems.push_back(childItem);
    }
}


теперь мне нужно в некоторых местах получить дочерние элементы, но определенного типа (применив какой-нибудь фильтр). Тут два варианта (как я вижу).
1) Сначала берем все дочерние, потом отфильтровываем

Код

// в одном месте напишем так
GetChildItems(allitems, childItems);
FilterByStatus(childitems);
...
// в другом так
GetChildItems(allitems, childItems);
FilterByCreationTime(childitems);


2) Отфильтруем элементы сразу в функции "Get..."
Код

typedef bool(CSomeClass::*filterFunction)(Item*) const;

void CSomeClass::GetChildItems( const vector<Item*> &allItems, vector<Item*> &childItems, filterFunction func)
{
   for(unsigned int i=0; i<allItems.size(); ++i)
   {
        Item *childItem = allItems[i].GetChild();
        if(childItem != 0 && (0 == func || (func && (this->*func)(childItem))))
            childItems.push_back(childItem);
    }
}


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

Автор: Amp 26.7.2012, 17:52
Первый вариант фильтрации просто памяти больше будет потреблять на больших коллекциях. Но идеологически, в разрезе проектирования API, наверное более предпочтителен. Ну а во втором варианте я бы передавал какие-нибудь std::function/lambda, нежели голый указатель на функцию.

Автор: xTr1m 26.7.2012, 17:56
Ну передается все по ссылкам, поэтому никаких копирований не предвидеться. Единственное в первом варианте нужно пройтись два раза по этой самой коллекции. 
А чем все же именно предпочтительнее? =))

Автор: Amp 26.7.2012, 18:00
Да как хочешь smile

Автор: xTr1m 26.7.2012, 18:02
Хорошо скажу так. Мне больше нравится второй вариант, однако его использование (указатели на функции + все же многократный вызов этой самой функции) в глазах начальства может выглядеть как "а зачем оно так нужно, если можно по-старинке, где все понятно и просто". Вот я и собираю аргументы.

Автор: borisbn 26.7.2012, 18:14
Мне второй вариант больше нравится... Обидно будет, если в коллекции, допустим, миллион элементов, а функция фильтрации оставит 2....
Кстати, функцию фильтрации, возможно, можно сделать универсальной. Не FilterByStatus или FilterByCreationTime, а
Код

template< class FieldType, FieldType Item::*FieldPtr >
struct FilterBy {
    FilterBy( const FieldType & value ) : m_value( value ) {}

   bool operator()( Item * item ) {
    if ( item->*FieldPtr == m_value ) {
        return true;
    }
private:
    const FieldType & m_value;
}
...
template< class FilterFunc >
void CSomeClass::GetChildItems( const vector<Item*> &allItems, vector<Item*> &childItems, FilterFunc func )
{
  for(unsigned int i=0; i<allItems.size(); ++i)
   {
        Item *childItem = allItems[i].GetChild();
        if ( childItem != 0 && func( childItem ) )
            childItems.push_back(childItem);
    }
}
...
// вызов
GetChildItems( allItems, childItems, FilterBy< int, &Item::status >( 0x42 ) ); 
GetChildItems( allItems, childItems, FilterBy< Time, &Item::creationTime >( Time( "21.12.2012" ) ); 


Добавлено @ 18:18
а добавив функцию NoFilter, получишь свою изначальную (не фильтрующую) функцию
Код

bool NoFilter( Item * ) {
    return true;
}
GetChildItems( allItems, childItems, NoFilter ); 

Автор: Result 26.7.2012, 18:46
Имхо, красивее посмотреть на компоновщик+итератор. 
Анкл Боб рекомендовал разбивать на функции выполняющие одно логическое действие, ну 
или отображать перечень в имени метода GetChildItemByFilter.
По поводу аргументов начальству, так это смотря что им нужно, костыли или архитектура.

Автор: borisbn 26.7.2012, 21:21
> красивее посмотреть на компоновщик+итератор.
Это как? Можно продемонстрировать кодом?

Автор: xTr1m 27.7.2012, 08:13
borisbn, за шаблонный вариант спасибо, но боюсь одно слово шаблон вызовет косые взгляды ан меня =))
Result, по поводу архитектуры. Вариант с шаблоном действительно явный пример архитектурного решения. В изначальном же варианте я не вижу явных аргументов, кроме как "это архитектура лучше", вот и хочу узнать чем. Может есть какой-нибудь сценарий развития, при котором в будущем мой 2ой вариант легче модернизировать, чем первый?

Автор: korian 27.7.2012, 09:08
Из того как я понял задачу, я считаю, что должны существовать обе функции.
Там, где нужны все child - дергаем одну, там где фильтрование - вторую.
Ну и второй вариант должен выглядеть примерно следующим образом (без всяких проверок и привязок):
Код

void CSomeClass::GetChildItems( const vector<Item*> &allItems, vector<Item*> &childItems, filterFunction func)
{
   for(unsigned int i=0; i<allItems.size(); ++i)
   {
        Item *childItem = allItems[i].GetChild();
        if(childItem != 0 && func(childItem))
            childItems.push_back(childItem);
    }
}


Можно сказать, что это копи-паст и тд. Но это нормальный копи-паст, потому что функции делают разное.
Пример такого подхода можно считать функцию erase в std::vector.

iterator erase ( iterator first, iterator last );
iterator erase ( iterator position );

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

Ну и в итоге можно сказать, что я за немного измененный второй вариант.

Аргументы:
Имея только одну функцию и реализацию по первому варианту - мы имеем преджевременную писсимизацию (тратим ОЗУ, чего можно не делать)
Имея только одну функцию и реализацию по второму варианту - ме имеем ту же преджевременную писсимизацию (тратим CPU на проверки, чего можно не делать)

Автор: Result 29.7.2012, 13:35
Цитата(borisbn @ 26.7.2012,  21:21)
Это как? Можно продемонстрировать кодом?

Вот примерчик накидал http://liveworkspace.org/code/d7c3105939a4cd952a826781a5ad649d 
Правда абстрактный пример не очень нагляден  smile 
Интересное представляет main.

Автор: mes 29.7.2012, 22:57
Цитата(korian @  27.7.2012,  08:08 Найти цитируемый пост)
впринципе, можно было бы обойтись только первой функцией. она полностью покрывает все потребности.
но удобство использования и возможность оптимизации при удалении одного элемента, заставляют иметь вторую.

эта потребность возникает вследствии "ограничения" доступа к древу обьектов функцией ГетЧайлдс.. Если предоставить константый доступ к ссылке на ветку, то можно будет применять стандартные алгоритмы.. Вобщем начинать надо не с вопроса какой сделать функцию, а с того как наиболее удобным образом представить данные... 

Автор: korian 30.7.2012, 15:59
Цитата(mes @  29.7.2012,  21:57 Найти цитируемый пост)
доступа к древу обьектов функцией ГетЧайлдс

Так это все-таки дерево? У меня как бы была мысль, но я не увидел никакого кода обхода дерева.

Автор: mes 30.7.2012, 22:24
список - частный случай древа smile

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