Vector and derived classes objects

M

ma740988

Consider a class - lets call it CObjectHolderClass that has a
collection of object pointers like

std::vector<CBaseClass*> base_class_vec;

as member var. Now somewhere in my CObjectHolderClass I do

CDerived1* p_derived1 = new CDerived1();
base_class_vec.push_back(derived1);

In CObjectHolderClass, i just use the public interface of CBaseClass.

If I derive another class - lets say CDerived2 from CBaseClass, I have
to change my CObjectHolderClass to do something like

CDerived2* p_derived2 = new CMyOtherBaseClass();
base_class_vec.push_back(p_derived2);

Trouble is, I have to modify CObjectHolderClass for different derived
classes. How do I get around this?

My guess is this borders on some type of 'design pattern' (an area I
haven't delved into yet) or perhaps a template solution but I'm
confused on how.

Untested or working source examples would be greatly appreaciated.
This alleaviates the need for me my potentially silly follow up
questions.

As always, thanks in advance.
 
V

Victor Bazarov

ma740988 said:
Consider a class - lets call it CObjectHolderClass that has a
collection of object pointers like

std::vector<CBaseClass*> base_class_vec;

as member var. Now somewhere in my CObjectHolderClass I do

CDerived1* p_derived1 = new CDerived1();
base_class_vec.push_back(derived1);

In CObjectHolderClass, i just use the public interface of CBaseClass.

If I derive another class - lets say CDerived2 from CBaseClass, I have
to change my CObjectHolderClass to do something like

CDerived2* p_derived2 = new CMyOtherBaseClass();
base_class_vec.push_back(p_derived2);

Trouble is, I have to modify CObjectHolderClass for different derived
classes. How do I get around this?

A template, perhaps...
My guess is this borders on some type of 'design pattern' (an area I
haven't delved into yet) or perhaps a template solution but I'm
confused on how.

Untested or working source examples would be greatly appreaciated.
This alleaviates the need for me my potentially silly follow up
questions.

#include <vector>

class Base {};

class ObjectHolder {
std::vector<Base*> base_class_vec;
public:
template<class D> void addDerived(D* d) {
base_class_vec.push_back(d);
}
};

class Derived1 : public Base {};
class Derived2 : public Base {};

int main() {
ObjectHolder blah;
blah.addDerived(new Derived1());
blah.addDerived(new Derived2());
}

(this example doesn't clean after itself, but you can add it, can't you?)

HTH

V
 
A

Alf P. Steinbach

* Victor Bazarov:
A template, perhaps...

The OP states that only the public interface of CBaseClass is used.

Hence, declaring the internal pointers as CBaseClass*, and passing in
an object factory serving such pointer, will do (here ignoring the probably
incorrect reference to CMyOtherBaseclass).

The main problem is that the examples the OP gives are not exception
safe; should be e.g.


std::auto_ptr<CBaseClass> p_derived = my_derived_factory->newObject();

base_class_vec.push_back( p_derived.get() ); // May throw.
p_derived.release();
 
M

ma740988

Victor Bazarov said:
ma740988 wrote: [...]

int main() {
ObjectHolder blah;
blah.addDerived(new Derived1());
blah.addDerived(new Derived2());
}

(this example doesn't clean after itself, but you can add it, can't you?)
That I could do. :) Thanks for the help
 
M

ma740988

[email protected] (Alf P. Steinbach) wrote in message news: said:
The OP states that only the public interface of CBaseClass is used.

Hence, declaring the internal pointers as CBaseClass*, and passing in
an object factory serving such pointer, will do (here ignoring the probably
incorrect reference to CMyOtherBaseclass).

The main problem is that the examples the OP gives are not exception
safe; should be e.g.


std::auto_ptr<CBaseClass> p_derived = my_derived_factory->newObject();

base_class_vec.push_back( p_derived.get() ); // May throw.
p_derived.release();

I thought of a auto_ptr solution though I might add I'm not sure if
I'm following you here but I'll investigate with a 'quick' example.
 
M

ma740988

[email protected] (Alf P. Steinbach) wrote in message news: said:
std::auto_ptr<CBaseClass> p_derived = my_derived_factory->newObject();

base_class_vec.push_back( p_derived.get() ); // May throw.
p_derived.release();

I'm unaware of how push_back will 'throw'. ie' I'm not aware/dont
know which exception is thrown if push_back fails or is it a no-op???
Could you elaborate.
 
V

Victor Bazarov

ma740988 said:
(e-mail address removed) (Alf P. Steinbach) wrote in message


I'm unaware of how push_back will 'throw'. ie' I'm not aware/dont
know which exception is thrown if push_back fails or is it a no-op???
Could you elaborate.

push_back may cause reallocation, which can fail, in that case
'std::bad_alloc'
is thrown. RTFM.

V
 
M

ma740988

base_class_vec.push_back( p_derived.get() ); // May throw.
p_derived.release();

I asked earlier about how push_back will throw. I realize now that
I've been mis-reading Josuttis and parts of the manual. I'm using
google newsreader so I'm unsure if your or anyone else responded
nonetheless, I understand now.
It boils down to this:

push_back may need to increase the size of the vector. If it does, it
will use its allocator, and the allocator could throw an exception if
it can't allocate the memory. The function will also use the
contained class's assignment operator, or possibly its copy
constructor, and that function could throw an exception. So the
push_back function will not throw an exception itself, (now comes the
part I was missing earlier) but it might call something else that
does.
 
M

ma740988

Victor Bazarov said:
push_back may cause reallocation, which can fail, in that case
'std::bad_alloc'
is thrown. RTFM.
I did and that was the problem.

Alf P. Steinbach wrote
The main problem is that the examples the OP gives are not exception
safe; should be e.g.
[/qoute]

Now given:

std::vector<CBaseClass*> base_class_vec;
// later
CDerived1* p_derived1 = new CDerived1();
base_class_vec.push_back(derived1);

Since I am using just vector of pointers, the copy-ctor or assign-op
wont be called for the objects the pointer points to, hence I dont see
any way an exception can be thrown there with my push_back.

Bottom line as I understand it:
If you dont provide your own allocator class, the push_back of
std::vector is a no-op on failure. It wont throw any exception if it
fails, Josuttis as also mentiones that in his STL book.
 
V

Victor Bazarov

ma740988 said:
[...]
Since I am using just vector of pointers, the copy-ctor or assign-op
wont be called for the objects the pointer points to, hence I dont see
any way an exception can be thrown there with my push_back.

Huh? push_back beyond the capacity of the vector _requires_ that the
storage for that vector is grown. That means reallocation. That also
means the original storage has to stay around until all the copying
has competed. If your vector occupies at least half the memory that
your process is allowed to have, there is no way in hell push_back can
succeed without throwing to tell you that you've run out of memory.
Bottom line as I understand it:
If you dont provide your own allocator class, the push_back of
std::vector is a no-op on failure. It wont throw any exception if it
fails, Josuttis as also mentiones that in his STL book.

I think you got this wrong. _If_ the push_back throws (to indicate the
failure), _then_ it's a NOP. IOW, the container doesn't change, all
iterators are valid, no reallocation happens. Reread that place in
Josuttis' book. I am 101% certain that all he writes there is the repeat
of 23.1/10. Just think a little: how can the push_back fail and _not_
throw an exception?

V
 
M

ma740988

Victor Bazarov said:
ma740988 said:
[...]
Since I am using just vector of pointers, the copy-ctor or assign-op
wont be called for the objects the pointer points to, hence I dont see
any way an exception can be thrown there with my push_back.

Huh? push_back beyond the capacity of the vector _requires_ that the
storage for that vector is grown. That means reallocation. That also
means the original storage has to stay around until all the copying
has competed. If your vector occupies at least half the memory that
your process is allowed to have, there is no way in hell push_back can
succeed without throwing to tell you that you've run out of memory.

[...]

Victor, appreaciate your patience but this reminds me of a course in
pertubation techniques where it took me 3 texts and days later before
I saw the light hence bear with me once more. I'm not arguing a throw
of bad_alloc, I understand. So now consider:

class X
{
std::string kdx;
public:
X() { }
};

int main()
{
X *ptr_x = new X();
std::size_t Jdx = sizeof (ptr_x);
std::cout << Jdx << std::endl;

std:vector<X*> my_vec;
my_vec.push_back(ptr_x);
std::cout << my_vec.size() << std::endl;
std::cout << my_vec.capacity() << std::endl;

delete ptr_x;
}

The output
4
1
1

on my platform. Now my_vec is a single byte. That said, I envision
it would require for me then to store a 'slew' of pointers (beyond the
capacity of the vectory - whatever number that is) to my_vec for me to
run out of memory. Correct?
 
K

Karl Heinz Buchegger

ma740988 said:
class X
{
std::string kdx;
public:
X() { }
};

int main()
{
X *ptr_x = new X();
std::size_t Jdx = sizeof (ptr_x);
std::cout << Jdx << std::endl;

std:vector<X*> my_vec;
my_vec.push_back(ptr_x);
std::cout << my_vec.size() << std::endl;
std::cout << my_vec.capacity() << std::endl;

delete ptr_x;
}

The output
4
1
1

on my platform. Now my_vec is a single byte.

How did you deduce that?
my_vec is certainly not a single byte.
That said, I envision
it would require for me then to store a 'slew' of pointers (beyond the
capacity of the vectory - whatever number that is) to my_vec for me to
run out of memory. Correct?

Right. But capacity has nothing to do with it. capacity is just the number
of array entries the vector has allocated right now. size() returns the
number of entries which are actually in use. If you continue to push_back
things to the vector, its size will grow until finally it reaches capacity()
(That is: all allocted entries have been used). When this happens, the vector
tries to increase capacity by allocating a larger junk of memory and continue.
It is clear that this cannot happen forever. Eventually the vector wil have
used up all available memory and ....

Look. Don't make things so complicated. A far amount of programming concepts
are taken directly from real live. Say you want to write some things down on paper.
What do you do? You take a notebook and start writing. Say you have written
(pushed back) 2 pages. So the capacity of the notebook is (lets say) 40 pages
(the total number of pages, some of them are unused) and the size of it (the
part that is used) is 2 pages. If you continue to write in your notebook, the size
increases to the capacity (the book gets full).
When this happens, what do you do? You increase the capacity by buying a notebook
with more pages. Since you don't want more then 1 notebook around, you first
copy the content from the old notebook to the new notebook. Then you continue
writing until the size has reached up to that capacity, etc., etc.
Eventually you will be in need for a notebook with so many pages, that you have
used up all the paper on the planet earth. When this happens, your notebook dealer
will tell you that you brought him into troubles getting such a large notebook, he
will throw an exception.

See the similarities?
 
M

ma740988

Karl Heinz Buchegger said:
How did you deduce that?
my_vec is certainly not a single byte.
Eeek!! I ruined that one.
Right. But capacity has nothing to do with it. capacity is just the number
of array entries the vector has allocated right now. size() returns the
number of entries which are actually in use. If you continue to push_back
things to the vector, its size will grow until finally it reaches capacity()
(That is: all allocted entries have been used). When this happens, the vector
tries to increase capacity by allocating a larger junk of memory and continue.
It is clear that this cannot happen forever. Eventually the vector wil have
used up all available memory and ....

Look. Don't make things so complicated. A far amount of programming concepts
are taken directly from real live. Say you want to write some things down on paper.
What do you do? You take a notebook and start writing. Say you have written
(pushed back) 2 pages. So the capacity of the notebook is (lets say) 40 pages
(the total number of pages, some of them are unused) and the size of it (the
part that is used) is 2 pages. If you continue to write in your notebook, the size
increases to the capacity (the book gets full).
When this happens, what do you do? You increase the capacity by buying a notebook
with more pages. Since you don't want more then 1 notebook around, you first
copy the content from the old notebook to the new notebook. Then you continue
writing until the size has reached up to that capacity, etc., etc.
Eventually you will be in need for a notebook with so many pages, that you have
used up all the paper on the planet earth. When this happens, your notebook dealer
will tell you that you brought him into troubles getting such a large notebook, he
will throw an exception.

See the similarities?

Indeed. My inital problem: I perused the standard and Josuttis, only
to find out by Victor that my interpretation was 'flawed'. As a
result I could not understand how a push back of - using your analogy
- 2 pages and 2 pages ONLY would result in a 'throw'. The words "or
no effect" literally had me wrapped around the axle. I know, I'm
caught up in 'details, details'. :)

In any event thanks for the patience.
An again, thank you all.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top