Здравствуйте, Anfi, Вы писали:
A>Помогите новичку в STL. A>Начитавшись корифеев STL, понял, что надо писать алгоритмы вместо циклов. A>Как, не используя циклы, можно вызвать функцию член производного класса A>для каждого элемента вектора указателей на базовый класс.
A>Иными словами, вызвать внутри STL-алгоритма функцию-член от производного класса A>т.е. призвести приведение типа ЭЛЕМЕНТА контейнера перед вызовом функции-члена?
A>#include <vector> A>#include <algorithm> A>#include <functional>
A>class A A>{ public: int Foo(){return 0;}; }; A>class B : public A A>{ public: int Boo(){return 1;}; };
A>int main() A>{ A> std::vector<A *> AV; A> // Заполняем контейнер AV указателями на производный класс B A> // ... A> std::for_each(AV.begin(), AV.end(), std::mem_fun<int,A>(&A::Foo)); // OK A> // Хочу привести тип к B и вызвать его функцию-член для всех элементов средствами STL A> std::for_each(AV.begin(), AV.end(), std::mem_fun<int,B>(&B::Boo)); // так нельзя ((, а как можно? A> return 0; A>}
A>Заранее благодарен, A> Анатолий.
А вы уверенны что в векторе будут все элементы типа B ?
Если да, то зачем хранить тип A* ?
Вы делаете приведение вниз по иерархии, а это чревато ошибками.
Пример :
A a
B b;
A* pa = &b;
((B*)pa)->Boo(); // OK
pa = &a;
((B*)pa)->Boo(); // BOOM !!!
Вариант 1 : сделать функцию Boo виртуальной.
Вариант 2 : хранить не A*, а B*.
Здравствуйте, Anfi, Вы писали:
A>Помогите новичку в STL. A>Начитавшись корифеев STL, понял, что надо писать алгоритмы вместо циклов. A>Как, не используя циклы, можно вызвать функцию член производного класса A>для каждого элемента вектора указателей на базовый класс.
A>Иными словами, вызвать внутри STL-алгоритма функцию-член от производного класса A>т.е. призвести приведение типа ЭЛЕМЕНТА контейнера перед вызовом функции-члена?
Ну а так-то почему нельзя сделать (если уж очень нужно):
#include <vector>
#include <algorithm>
#include <functional>
class A
{ public: int Foo(){return 0;}; };
class B : public A
{ public: int Boo(){return 1;}; };
void
call_Boo( A * p )
{
((B *)p)->Boo();
}
int main()
{
std::vector<A *> AV;
// Заполняем контейнер AV указателями на производный класс B
// ...
std::for_each(AV.begin(), AV.end(), std::mem_fun<int,A>(&A::Foo));
std::for_each(AV.begin(), AV.end(), call_Boo );
return 0;
}
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Вызов функции произв.класса внутри STL-алгоритма
Помогите новичку в STL.
Начитавшись корифеев STL, понял, что надо писать алгоритмы вместо циклов.
Как, не используя циклы, можно вызвать функцию член производного класса
для каждого элемента вектора указателей на базовый класс.
Иными словами, вызвать внутри STL-алгоритма функцию-член от производного класса
т.е. призвести приведение типа ЭЛЕМЕНТА контейнера перед вызовом функции-члена?
class A
{ public: int Foo(){return 0;}; };
class B : public A
{ public: int Boo(){return 1;}; };
int main()
{
std::vector<A *> AV;
// Заполняем контейнер AV указателями на производный класс B
// ...
std::for_each(AV.begin(), AV.end(), std::mem_fun<int,A>(&A::Foo)); // OK
// Хочу привести тип к B и вызвать его функцию-член для всех элементов средствами STL
std::for_each(AV.begin(), AV.end(), std::mem_fun<int,B>(&B::Boo)); // так нельзя ((, а как можно?
return 0;
}
Заранее благодарен,
Анатолий.
Re: Вызов функции произв.класса внутри STL-алгоритма
#include <algorithm>
template <class InputIterator, class Function>
void for_each(InputIterator first, InputIterator last,
Function f);
Description
The for_each algorithm applies function f to all members of the sequence in the range [first, last), where first and last are iterators that define the sequence. Since this a non-mutating algorithm, the function f cannot make any modifications to the sequence, but it can achieve results through side effects (such as copying or printing). If f returns a result, the result is ignored.
Complexity
The function f is applied exactly last — first times.
Example
//
// for_each.cpp
//
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
// Function class that outputs its argument times x
template <class Arg>
class out_times_x : private unary_function<Arg,void>
{
private:
Arg multiplier;
public:
out_times_x(const Arg& x) : multiplier(x) { }
void operator()(const Arg& x)
{ cout << x * multiplier << " " << endl; }
};
int main()
{
int sequence[5] = {1,2,3,4,5};
// Set up a vector
vector<int> v(sequence,sequence + 5);
// Setup a function object
out_times_x<int> f2(2);
for_each(v.begin(),v.end(),f2); // Apply function
return 0;
}
Program Output
2
4
6
8
10
Warnings
If your compiler does not support default template parameters, then you always need to supply the Allocator template argument. For instance, you have to write:
vector<int, allocator<int> >
instead of:
vector<int>
If your compiler does not support namespaces, then you do not need the using declaration for std.
Re: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, Anfi, Вы писали:
A>Помогите новичку в STL. A>Начитавшись корифеев STL, понял, что надо писать алгоритмы вместо циклов. A>Как, не используя циклы, можно вызвать функцию член производного класса A>для каждого элемента вектора указателей на базовый класс.
A>Иными словами, вызвать внутри STL-алгоритма функцию-член от производного класса A>т.е. призвести приведение типа ЭЛЕМЕНТА контейнера перед вызовом функции-члена?
Как написал _nn_, непонятно, зачем тебе держать указатели на базовый класс A, если в векторе будут все равно будут храниться исключительно указатели на производный класс B (иначе вот эта твоя конструкция просто рухнет в рантайме:
Но это сделано в предволожении, что вектор храниться в памяти непрерывном куском (стандартом это гарантируется). Мы руками привели тип vector<A*>::iterator к типу B**, и в алгоритме for_each инкремент происходит указателя, а не итератора
"Что не завершено, не сделано вовсе" Гаусс
Re[2]: Вызов функции произв.класса внутри STL-алгоритма
От:
Аноним
Дата:
11.03.05 10:58
Оценка:
H>Поставь себе хоть как-то перевариваемую документацию, глядишь, ряд вопросов отпадет! H>Удачи!
Документацию чего ты ему советуешь ставить??? Приведенный тобой пример решает проблему автора вопроса?
Re[3]: Вызов функции произв.класса внутри STL-алгоритма
а откуда по-твоему люди инфу берут?
Конечно, разные приседания и танцы с бубном — дело творческое, но если приходится, то возникает вопрос в корректности написания...
Re[4]: Вызов функции произв.класса внутри STL-алгоритма
От:
Аноним
Дата:
11.03.05 11:11
Оценка:
H>а откуда по-твоему люди инфу берут? H>Конечно, разные приседания и танцы с бубном — дело творческое, но если приходится, то возникает вопрос в корректности написания...
Дак ты так и не сказал, что за документацию.
Re[2]: Вызов функции произв.класса внутри STL-алгоритма
S>Но это сделано в предволожении, что вектор храниться в памяти непрерывном куском (стандартом это гарантируется). Мы руками привели тип vector<A*>::iterator к типу B**, и в алгоритме for_each инкремент происходит указателя, а не итератора
Зато нет гарантий, что итератор реализован как указатель на объект, поэтому такое приведение (B**)v.begin() в общем случае не верно (может работать только на конкретных реализациях STL), лучше заменить (B**)v.begin(), на (B**)&v[0]
Re[2]: Вызов функции произв.класса внутри STL-алгоритма
Спасибо, но я хотел совсем по-честному, силами STL.
Дело в том, что оба класса в реальной программе чистые виртуальные, и всё обобщение идет на уровне
класса B со стороны его потомков.
Замысел был такой: для класса A реализован класс VectorA:public vector<A*>, который выполняет кое-какую работу.
Затем от класса A наследуется класс B, а от VectorA — класс VectorB:public VectorA,
НО VectorB ССЫЛАЕТСЯ ПО-ПРЕЖНЕМУ на указатель A, т.е. его итератор работает не с B*, а с A*.
Как можно классу VectorB объяснить, что его итератор работает с B*; например, перегрузить итератор (?).
В пределах одного контейнера указатели разных типов не смешиваются и тип элемента точно известен в момент обращения.
Поэтому я бы хотел делать явное преобразование типа A* к B*, но совершенно не понимаю, как это можно корректно сделать в принятом в STL синтаксисе.
P.S. Вариант sadomovalex'а:
S>for_each((B**)v.begin(), (B**)v.end(), mem_fun<int,B>(&B::Boo));
гораздо лучше того, до которого я сам додумался, мой — совсем зверский:
Hello, Anfi!
You wrote on Fri, 11 Mar 2005 10:15:26 GMT:
A> #include <vector> A> #include <algorithm> A> #include <functional>
A> class A A> { public: int Foo(){return 0;}; }; A> class B : public A A> { public: int Boo(){return 1;}; };
A> int main() A> { A> std::vector<A *> AV; A> // Заполняем контейнер AV указателями на производный класс B A> // ... A> std::for_each(AV.begin(), AV.end(), std::mem_fun<int,A>(&A::Foo)); // A> OK// Хочу привести тип к B и вызвать его функцию-член для всех элементов A> средствами STLstd::for_each(AV.begin(), AV.end(), A> std::mem_fun<int,B>(&B::Boo)); // так нельзя ((, а как можно?return A> 0; }
Если уж такой изврат зачем-то все-таки понадобился, то его можно реализовать
так:
#include <vector>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
class A
{ public: int Foo(){return 0;}; };
class B : public A
{ public: int Boo(){return 1;}; };
B* theCast(A*a)
{ return static_cast<B*>(a); }
void test(std::vector<A *> &AV)
{
std::for_each(boost::make_transform_iterator(AV.begin(), theCast),
boost::make_transform_iterator(AV.end(), theCast),
std::mem_fun<int,B>(&B::Boo));
}
With best regards, Sergey Skorniakov. E-mail: s.skorniakov@megaputer.ru
Posted via RSDN NNTP Server 1.9
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Вызов функции произв.класса внутри STL-алгоритма
Hello, Anfi!
You wrote on Fri, 11 Mar 2005 11:58:24 GMT:
A> Замысел был такой: для класса A реализован класс VectorA:public A> vector<A*>, который выполняет кое-какую работу. Затем от класса A A> наследуется класс B, а от VectorA — класс VectorB:public VectorA, НО A> VectorB ССЫЛАЕТСЯ ПО-ПРЕЖНЕМУ на указатель A, т.е. его итератор A> работает не с B*, а с A*.
Ашипка. Если уж так охота унаследоваться от std::vector, то лучше сделать
чуток по другому:
template <class T> Vector : public std::vector<T>
{
...// Ну и тут собственно потроха
};
И использовать Vector<A*> или Vector<B*>.
Или вообще параметризовать шаблон контейнером (мало ли — std::vector
разонравится и захочется его поменять на map)
template <class V> Vector : public V
{
...// Ну и тут собственно потроха
};
И использовать Vector<std::vector<A*> > или Vector<std::vector<B*> >.
With best regards, Sergey.
Posted via RSDN NNTP Server 1.9
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, Anfi, Вы писали:
A>Спасибо, но я хотел совсем по-честному, силами STL. A>Дело в том, что оба класса в реальной программе чистые виртуальные, и всё обобщение идет на уровне A>класса B со стороны его потомков.
про наследование от классов STL — на всякий случай
Далее — тебе нужно расширить реализацию stl-ного вектора на случай, когда этот вектор параметризуется указателями на A. И уже затем расширить функциональность последнего на случай параметризацией указателями на B (причем B наследуется от A). Это можно сделать так:
#include <vector>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>
class A {public:int Foo(){return 0;}};
class B : public A {public: int Boo(){return 1;}};
template <class T>
class VectorA: public boost::enable_if<boost::is_convertible<T, A*>, std::vector<T> >::type
{
public:
void FooInVectorA() {}
};
class VectorB: public VectorA<B*>
{
public:
void FooInVectorB() {}
};
int main()
{
VectorB v;
v.push_back(new B());
v.FooInVectorA();
v.FooInVectorB();
return 0;
}
В этом случае вектор VectorB у тебя будет наследным от std::vector<B*>, а не от std::vector<A*> — следовательно никаких проблем с преобразованием типов в алгоритмах не будет. И в то же время VectorB содержит в себе всю функциональность класса VectorA
"Что не завершено, не сделано вовсе" Гаусс
Re[4]: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, sadomovalex, Вы писали:
S>Здравствуйте, Anfi, Вы писали:
A>>Спасибо, но я хотел совсем по-честному, силами STL. A>>Дело в том, что оба класса в реальной программе чистые виртуальные, и всё обобщение идет на уровне A>>класса B со стороны его потомков.
А если ты B унаследовал от A, только для того, чтобы потом унаследовать их специализированные версии векторов, то лучше вообще не смешивать A и B, а сделать так:
#include <vector>
#include <algorithm>
#include <functional>
class A {public:int Foo(){return 0;}};
class B {public:int Boo(){return 1;}};
template <class T>
class VectorBase: public std::vector<T>
{
public:
void FooInVectorBase() {}
};
class Vector: public VectorBase<B*>
{
public:
void FooInVector() {}
};
int main()
{
Vector v;
v.push_back(new B());
v.push_back(new B());
std::for_each(v.begin(), v.end(), std::mem_fun<int,B>(&B::Boo));
// ...return 0;
}
"Что не завершено, не сделано вовсе" Гаусс
Re[2]: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, eao197, Вы писали:
E>Ну а так-то почему нельзя сделать (если уж очень нужно): E>void call_Boo( A * p ) { ((B *)p)->Boo(); } E>std::for_each(AV.begin(), AV.end(), call_Boo );
Можно, конечно, но "неаккуратненько как-то", тем более, что производных классов предполагается много
(>100) и выглядеть это будет не очень наглядно и читабельно.
По той же причине не хочется использовать и шаблоны.
Спасибо Sergey за решение с Boost'ом !
(правда, у меня под VC6.0 sp6 почему-то не компилится) %-(
Re[3]: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, Anfi, Вы писали:
A>Здравствуйте, eao197, Вы писали:
E>>Ну а так-то почему нельзя сделать (если уж очень нужно): E>>void call_Boo( A * p ) { ((B *)p)->Boo(); } E>>std::for_each(AV.begin(), AV.end(), call_Boo );
A>Можно, конечно, но "неаккуратненько как-то", тем более, что производных классов предполагается много A>(>100) и выглядеть это будет не очень наглядно и читабельно.
По вашему будет аккуратненько, если вы вызываете невиртуальные методы из производного класса применяя при этом upcast?
К тому же в решении Sergey
Здравствуйте, Anfi, Вы писали:
A>Здравствуйте, eao197, Вы писали:
E>>Ну а так-то почему нельзя сделать (если уж очень нужно): E>>void call_Boo( A * p ) { ((B *)p)->Boo(); } E>>std::for_each(AV.begin(), AV.end(), call_Boo );
A>Можно, конечно, но "неаккуратненько как-то", тем более, что производных классов предполагается много A>(>100) и выглядеть это будет не очень наглядно и читабельно.
#include <vector>
#include <algorithm>
#include <functional>
class A
{ public: int Foo(){return 0;}; };
class B : public A
{ public: int Boo(){return 1;}; };
template< class Base, class Derived >
struct call_method_t
{
typedef int (Derived:: *pfn_t)();
pfn_t m_method;
call_method_t( pfn_t method )
: m_method( method )
{}
void
operator()( Base * a )
{
((static_cast< Derived * >(a))->*m_method)();
}
};
int main()
{
std::vector<A *> AV;
std::for_each(AV.begin(), AV.end(), std::mem_fun<int,A>(&A::Foo)); // OK
std::for_each(AV.begin(), AV.end(), call_method_t< A, B >( &B::Boo ) );
return 0;
}
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: Вызов функции произв.класса внутри STL-алгоритма
Здравствуйте, eao197, Вы писали:
E>По вашему будет аккуратненько, если вы вызываете невиртуальные методы из производного класса применяя при этом pcast?
По-моему будет совсем "неаккуратненько" :-).
По этому и мучаюсь вопросами,
и с благодарностью принимаю Ваши соображения по этому поводу.
История вопроса такова:
я сейчас переделываю базовые классы достаточно крупного проекта на BCB6.0 так,
чтобы код был 1) более быстр 2) более переносим — на VC/gcc, в частности.
В BCB6.0 списки базировались на TList'е, с которым пришлось расстаться из-за его Паскалевского происхождения.
В связи с этим дозрел до STL и до необходимости перестройки иерархии базовых классов.
В проекте более 100 конечных классов, основанных на классе B и очень большой кусок функциональности можно реализовать на уровне этого класса и списка vector<B*>.
НО! Для производных классов существует огромное количество частного, относящегося к ним кода, который так и просится быть переведенным на язык алгоритмов STL.
Была попытка использоват шаблоны, которая провалилась из-за того, что специализация функций шаблона для
большого числа классов намертво убивала Борландовский линковщик. При числе классов >80 и числе файлов проекта ~>50
линкер вылетал с сообщением о нехватке памяти (на машине при этом по показаниям TaskManager'а было свободно нем менее 400 Мб оперативки). Кроме того, каждый OBJ файл занимал много мегабайт, поэтому с шаблонами пришлось завязать и использовать их по минимуму...
E>Здесь при наличии большого количество прозводных классов решение в корне менять нужно, а не костыли придумывать.
Так что еще раз спасибо за помощь!
Буду думать дальше...
Анатолий.
Обрати внимание, что там в call_method_t::pfn_t жестко зашита сигнатура метода. Если не все методы будут удовлетворять этой сигнатуре, то придется что-то додумывать.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.