473,237 Members | 1,173 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,237 software developers and data experts.

Vector of abstract classes filled with derived classes ?

Hi!

If I've a vector filled with abstract classes, can I push in it the
derived classes too? Even if derived classes have new methods?

I've done some experiments, and it seem I can push the derived classes,
but I can use them only calling the methods declared in abstract class
(the new methods declared only in derived classes return an error).

Please take a look to my code (hey, this is not an homework!
I'm studying C++ to port this opensource project from python to C++
http://www.makehuman.org):

This is my abstract class:

--------------------------------------------
class mhwidget
{

public:
virtual void draw()= 0;
virtual ~mhwidget() {}

};
--------------------------------------------

In another file, I've defined a vector for this class:

--------------------------------------------
std::vector<mhwidget*> widgetList;
--------------------------------------------

I fill this vector, and use the "draw()" method,
in this way:

--------------------------------------------
//Put widget into container
void mhcontainer::addWidget(mhwidget* w)
{
widgetList.push_back(w);
}

//Draw all widgets
void mhcontainer::drawAll()
{

std::vector<mhwidget*>::iterator it = widgetList.begin();
while(it != widgetList.end())
{
(*it++)->draw();
}
}
--------------------------------------------

I've written an example of derived class (this is only the header):

---------------------------------------------
class square : public mhwidget
{
public:
virtual void draw();
};
---------------------------------------------

and this seem to work (from application/client code):

---------------------------------------------
mhcontainer contn;
contn.addWidget(new square);
contn.drawAll();
---------------------------------------------

but I'm not sure it's a clean C++ code.

If I add a new method in "square" and try to use it, like
while(it != widgetList.end())
{
(*it++)->draw();
(*it++)->newMethod();
}
it return an error, but this make sense...

thx,

Manuel
Jan 5 '06 #1
8 18429
Manuel wrote:
This is my abstract class:

--------------------------------------------
class mhwidget
{

public:
virtual void draw()= 0;
virtual ~mhwidget() {}

};
--------------------------------------------

In another file, I've defined a vector for this class:

--------------------------------------------
std::vector<mhwidget*> widgetList;
--------------------------------------------

I've written an example of derived class (this is only the header):

---------------------------------------------
class square : public mhwidget
{
public:
virtual void draw();
};
---------------------------------------------

and this seem to work (from application/client code):

---------------------------------------------
mhcontainer contn;
contn.addWidget(new square);
contn.drawAll();
---------------------------------------------

but I'm not sure it's a clean C++ code.

It's reasonable, just make sure you delete all elements in your
container. Or better yet, use a vector of shared pointer to the base
class (instead of normal pointer).

If I add a new method in "square" and try to use it, like
while(it != widgetList.end())
{
(*it++)->draw();
(*it++)->newMethod();
}
it return an error, but this make sense...

Yes, this is an error, because widgetList is a vector of <mhwidget*>,
not <square*>. Although you have a 'square' object inside, you might
also have *other* derived types, e.g., 'circle' which does not have
'newMethod'. So when you have a vector<mhwidget*>, you can only call
methods from 'mhwidget'.

One solution is to use dynamic cast:

square *sq = dynamic_cast<square*>(*it);
if (sq) {
sq->newMethod();
}

So, if the item in the vector happens to be a square, then invoke
'newMethod' using the pointer obtained from the dynamic cast. If it is
not a square, then the item will be ignored. But this is not very good
way to design your class heirarchy, because you are making users of
'widgetList' know about one of the possible implementations of
'mhwidget' and code specifically for that implementation. It is better
to remain general instead of being specific.

Hope this helps,
-shez-

Jan 5 '06 #2
Manuel wrote:
Hi!

If I've a vector filled with abstract classes,
You cannot fill a vector with classes, only with objects. You also can't
fill it with instances of abstract classes, so I assum you are talking
about pointers.
can I push in it the derived classes too?
Yes.
Even if derived classes have new methods?
Yes.
I've done some experiments, and it seem I can push the derived classes,
but I can use them only calling the methods declared in abstract class
(the new methods declared only in derived classes return an error).
Well, how would the compiler know at compile time (i.e. when it sees your
function call) which actual class the object you are accessing will be at
runtime? How should it know which member functions are available?
Please take a look to my code (hey, this is not an homework!
I'm studying C++ to port this opensource project from python to C++
http://www.makehuman.org):

This is my abstract class:

--------------------------------------------
class mhwidget
{

public:
virtual void draw()= 0;
virtual ~mhwidget() {}

};
--------------------------------------------

In another file, I've defined a vector for this class:

--------------------------------------------
std::vector<mhwidget*> widgetList;
So it is - as I assumed - a vector of pointers to mhwidget.
--------------------------------------------

I fill this vector, and use the "draw()" method,
in this way:

--------------------------------------------
//Put widget into container
void mhcontainer::addWidget(mhwidget* w)
{
widgetList.push_back(w);
}

//Draw all widgets
void mhcontainer::drawAll()
{

std::vector<mhwidget*>::iterator it = widgetList.begin();
while(it != widgetList.end())
{
(*it++)->draw();
}
}
--------------------------------------------

I've written an example of derived class (this is only the header):

---------------------------------------------
class square : public mhwidget
{
public:
virtual void draw();
};
---------------------------------------------

and this seem to work (from application/client code):

---------------------------------------------
mhcontainer contn;
contn.addWidget(new square);
contn.drawAll();
---------------------------------------------

but I'm not sure it's a clean C++ code.
This looks fine.
If I add a new method in "square" and try to use it, like
while(it != widgetList.end())
{
(*it++)->draw();
(*it++)->newMethod();
}
Are you sure that you want to call draw() only for every second object?
it return an error, but this make sense...


What is the programm supposed to do with those objects that are not of
dynamic type "square"? If you're accessing the object through mhwidget's
interface, only that is available.
If you want newMethod() be called where draw() is called, simply call it
from square::draw().
An alternative (which is in many situations considered as a design flaw in
C++) would be to use a dynamic cast, like:

while(it != widgetList.end())
{
(*it++)->draw();

square* sq = dynamic_cast<square*>(*it++);
if (sq)
sq->newMethod();
}

Jan 5 '06 #3
Manuel wrote:
Hi!

If I've a vector filled with abstract classes,
You can not fill any container with abstract classes and with classes
too. But you can fill a container with instances of (non-abstract)
classes. Second, you can fill container with references to instances of
concrete classes through pointers to abstract class.

can I push in it thederived classes too?
Only when you store C++ references or pointers to abstarct type.

Even if derived classes have new methods?
It doesn't matter.
I've done some experiments, and it seem I can push the derived
classes,
Not classes but objects.
but I can use them only calling the methods declared in
abstract class (the new methods declared only in derived classes
return an error).
Yes, abstract class is an interface shared with concrete classes.
In order to get access to some specific operations for concrete type
then use "cast pointer-to-base type to pointer-to-derived type".
In another file, I've defined a vector for this class:

std::vector<mhwidget*> widgetList;
This is vector in which you can store elements of type of
pointer-to-class-mhwidget. Here, the stored type is a pointer, not a class.
I fill this vector, and use the "draw()" method, in this way:

-------------------------------------------- //Put widget into
container void mhcontainer::addWidget(mhwidget* w) {
widgetList.push_back(w); }

//Draw all widgets void mhcontainer::drawAll() {

std::vector<mhwidget*>::iterator it = widgetList.begin(); while(it !=
widgetList.end()) { (*it++)->draw(); } }
--------------------------------------------

I've written an example of derived class (this is only the header):

--------------------------------------------- class square : public
mhwidget { public: virtual void draw(); };
---------------------------------------------

and this seem to work (from application/client code):

--------------------------------------------- mhcontainer contn;
contn.addWidget(new square); contn.drawAll();
---------------------------------------------

but I'm not sure it's a clean C++ code.
From the first sight it looks good (thought, I've not
tried to compile it). Dynamic polymorphism can be achived through
abstract types with virtual operations.
If I add a new method in "square" and try to use it, like while(it !=
widgetList.end()) { (*it++)->draw(); (*it++)->newMethod(); } it
return an error, but this make sense...

You need to do static_cast<some_derived_class*>(*it)->newMethod();

Cheers
--
Mateusz Łoskot
http://mateusz.loskot.net
Jan 5 '06 #4
Thanks to all.
The dynamic cast is not needed, because the widget interface is
sufficient for this simple GUI. The only important thing is using
pointers to abstract widget to store the derived objs too, is ok.

Thanks!

Manuel
Jan 5 '06 #5
Manuel wrote:
Thanks to all.
The dynamic cast is not needed, because the widget interface is
sufficient for this simple GUI. The only important thing is using
pointers to abstract widget to store the derived objs too, is ok.


That's probably the most common way of storing GUI objects (by pointer),
and sticking pointers to base classes in a container is pretty much the
only thing you CAN do when you need to access them polymorphically.

The problem is often that of who does the cleaning up (deleting the
pointer) and when... this is often managed by either a container of
boost::shared_ptr<objectType> or one of the boost ptr_containers.

However, in a GUI situation, often the parent "window" deals with the
cleanup, so you're probably ok.

Ben Pope
--
I'm not just a number. To many, I'm known as a string...
Jan 5 '06 #6
Ben Pope wrote:
Manuel wrote:
Thanks to all.
The dynamic cast is not needed, because the widget interface is
sufficient for this simple GUI. The only important thing is using
pointers to abstract widget to store the derived objs too, is ok.


That's probably the most common way of storing GUI objects (by pointer),
and sticking pointers to base classes in a container is pretty much the
only thing you CAN do when you need to access them polymorphically.

The problem is often that of who does the cleaning up (deleting the
pointer) and when... this is often managed by either a container of
boost::shared_ptr<objectType> or one of the boost ptr_containers.


I recommend using a cow_ptr smart pointer over using the boost
ptr_containers.
http:://code.axter.com/cow_ptr.h
With the cow_ptr you don't have to add a clone function to the target
type, and the cow_ptr can be use with any of the main STL containers.

Also, the boost ptr_containers causes a lot of compiler warnings when
using it with compilers like VC++ 7.x, and some of the boost pointer
containers will not compile on VC++ 6.0, or other prestandard
compilers.

The cow_ptr is more portable, and can be used more generically.

Jan 5 '06 #7

Axter wrote:
I recommend using a cow_ptr smart pointer over using the boost
ptr_containers.
http:://code.axter.com/cow_ptr.h
With the cow_ptr you don't have to add a clone function to the target
type, and the cow_ptr can be use with any of the main STL containers.

Also, the boost ptr_containers causes a lot of compiler warnings when
using it with compilers like VC++ 7.x, and some of the boost pointer
containers will not compile on VC++ 6.0, or other prestandard
compilers.

The cow_ptr is more portable, and can be used more generically.


Another option is not to use smart-pointers at all, but to use a
parent-child relationship. That does mean there is a slight intrusion,
i.e. your child has to be derived from a specific base class that knows
of an abstract "parent" type. The child is created adding itself to its
parent. When the parent is destroyed it deletes all its children.

The parent class needs only be implemented once. The parent itself does
not have to be a child but in the case of GUI programming they often
will be.

parent can implement with a vector of weak pointers and its destructor
can delete all the elements in the list. parent should obviously be
non-copyable and non-assignable.

By the way, you can write specific derivatives of parent that handle
their children in different ways. (parent itself should be abstract).
And you can have different types.

Jan 5 '06 #8
Ben Pope wrote:

The problem is often that of who does the cleaning up (deleting the
pointer) and when... this is often managed by either a container of
boost::shared_ptr<objectType> or one of the boost ptr_containers.


To delete all widgets, I'written the function deletaAll(). This is the
full code of container of widgets. Really I'm not sure it work: I've
modified an example (thanks to Mateusz) that worked, but I've not fully
understand functors, so maybe I've ruined all, using them in a function.
What do you think? Maybe OK?

---------------------------------------------------

#include <windows.h>
#include "mhcontainer.h"
#include <iostream>//For debug

struct DeleteObjs
{
template <typename T>
void operator()(const T* ptr) const
{
delete ptr;
}
};

//Put widget into container
void mhcontainer::addWidget(mhwidget* w)
{
std::cout << "added";
widgetList.push_back(w);
std::cout << "now the size is: " << widgetList.size() << std::endl ;
}

//Draw all widgets
void mhcontainer::drawAll()
{

std::vector<mhwidget*>::iterator it = widgetList.begin();
while(it != widgetList.end())
{
std::cout << "widget!";
(*it++)->draw();
//(*it++)->tryMe();
}
}

//Delete all widgets
void mhcontainer::deleteAll()
{
std::for_each(widgetList.begin(), widgetList.end(), DeleteObjs());
}
Jan 5 '06 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: Christian Christmann | last post by:
Hi, I've a class Handler which contains a STL list std::list<Abstract*> mAbstract; which is storing elements of the abstract class Abstract. Further this class has a getElement function to...
18
by: Bradley | last post by:
I'm trying to determine if there's a general rule for when an Interface should used vs. an Abstract Class. Is there any design advantage to using one or the other? Brad
0
by: Craig Buchanan | last post by:
i am trying to build an application that uses plugins to extend the business functionality of an application. i've decided to use inheritance of abstract classes as the mechanism to do this. ...
7
by: jason | last post by:
In the microsoft starter kit Time Tracker application, the data access layer code consist of three cs files. DataAccessHelper.cs DataAcess.cs SQLDataAccessLayer.cs DataAcccessHelper appears...
2
by: talkingpidgin | last post by:
I am trying to figure out why it is not conventional to use protected constructors in abstract classes since the only time they should be called is by the constructors of it's derived classes. Is...
3
by: nw | last post by:
Hi, I have three classes, a template pure virtual base class, a template derived class and a third which I would like to use to store copies of the derived class. The code looks like this: ...
2
by: Zytan | last post by:
I know that WebRequest.GetResponse can throw WebException from internet tutorials. However in the MSDN docs: http://msdn2.microsoft.com/en-us/library/system.net.webrequest.getresponse.aspx It...
4
by: David Zha0 | last post by:
Hi, "when we call a virtual method, the runtime will check the instance who called the method and then choose the suitable override method, this may causes the performance drop down", is this...
3
by: ramsisiv | last post by:
I know that it is possible but, if the abstract class has a virtual function print, that prints info about the objekt and the derived classes each have theire own version of print, will a print...
3
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 3 Jan 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). For other local times, please check World Time Buddy In...
0
by: jianzs | last post by:
Introduction Cloud-native applications are conventionally identified as those designed and nurtured on cloud infrastructure. Such applications, rooted in cloud technologies, skillfully benefit from...
0
by: abbasky | last post by:
### Vandf component communication method one: data sharing ​ Vandf components can achieve data exchange through data sharing, state sharing, events, and other methods. Vandf's data exchange method...
0
Git
by: egorbl4 | last post by:
Скачал я git, хотел начать настройку, а там вылезло вот это Что это? Что мне с этим делать? ...
1
by: davi5007 | last post by:
Hi, Basically, I am trying to automate a field named TraceabilityNo into a web page from an access form. I've got the serial held in the variable strSearchString. How can I get this into the...
0
by: MeoLessi9 | last post by:
I have VirtualBox installed on Windows 11 and now I would like to install Kali on a virtual machine. However, on the official website, I see two options: "Installer images" and "Virtual machines"....
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: Aftab Ahmad | last post by:
Hello Experts! I have written a code in MS Access for a cmd called "WhatsApp Message" to open WhatsApp using that very code but the problem is that it gives a popup message everytime I clicked on...
0
by: Aftab Ahmad | last post by:
So, I have written a code for a cmd called "Send WhatsApp Message" to open and send WhatsApp messaage. The code is given below. Dim IE As Object Set IE =...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.