Vector and derived classes objects

Discussion in 'C++' started by ma740988, Nov 5, 2004.

  1. ma740988

    ma740988 Guest

    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.
     
    ma740988, Nov 5, 2004
    #1
    1. Advertising

  2. ma740988 wrote:
    > 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
     
    Victor Bazarov, Nov 5, 2004
    #2
    1. Advertising

  3. * Victor Bazarov:
    > ma740988 wrote:
    > > 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...


    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();

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Nov 5, 2004
    #3
  4. ma740988

    ma740988 Guest

    Victor Bazarov <> wrote in message news:<QQNid.8009$09.us.to.verio.net>...
    > 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
     
    ma740988, Nov 6, 2004
    #4
  5. ma740988

    ma740988 Guest

    (Alf P. Steinbach) wrote in message news:<>...
    [...]
    > > 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();


    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.
     
    ma740988, Nov 6, 2004
    #5
  6. ma740988

    ma740988 Guest

    (Alf P. Steinbach) wrote in message news:<>...
    [...]
    >
    >
    > 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.
     
    ma740988, Nov 7, 2004
    #6
  7. "ma740988" <> wrote...
    > (Alf P. Steinbach) wrote in message
    > news:<>...
    > [...]
    >>
    >>
    >> 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.


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

    V
     
    Victor Bazarov, Nov 7, 2004
    #7
  8. ma740988

    ma740988 Guest

    (Alf P. Steinbach) wrote in message news:<>...
    [...]

    > 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.
     
    ma740988, Nov 7, 2004
    #8
  9. ma740988

    ma740988 Guest

    "Victor Bazarov" <> wrote in message news:<77tjd.57469$HA.40359@attbi_s01>...
    [...]
    > 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
     
    ma740988, Nov 8, 2004
    #9
  10. ma740988 wrote:
    > [...]
    > 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
     
    Victor Bazarov, Nov 8, 2004
    #10
  11. ma740988

    ma740988 Guest

    Victor Bazarov <> wrote in message news:<aiMjd.8329$09.us.to.verio.net>...
    > ma740988 wrote:
    > > [...]
    > > 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?
     
    ma740988, Nov 9, 2004
    #11
  12. ma740988 wrote:
    >
    >
    > 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?

    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Nov 9, 2004
    #12
  13. ma740988

    ma740988 Guest

    Karl Heinz Buchegger <> wrote in message
    [...]
    >
    > How did you deduce that?
    > my_vec is certainly not a single byte.

    Eeek!! I ruined that one.

    >
    > > 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?


    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.
     
    ma740988, Nov 10, 2004
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Manuel
    Replies:
    8
    Views:
    640
    Manuel
    Jan 5, 2006
  2. Replies:
    8
    Views:
    1,932
    Csaba
    Feb 18, 2006
  3. Replies:
    1
    Views:
    397
    myork
    May 23, 2007
  4. Replies:
    1
    Views:
    389
    Victor Bazarov
    May 23, 2007
  5. H
    Replies:
    4
    Views:
    362
    terminator
    Jul 18, 2007
Loading...

Share This Page