heap vs. stack

Discussion in 'C++' started by Brian Buderman, Dec 18, 2006.

  1. Consider the creation of a class library, where the desire is to allow
    the user of the library to chain objects together during construction in
    a single statement. For example, this statement uses the heap:

    topLevelObject = new type1(new type2(new type3(), new type4());

    While this example only uses 4 types, in actuality there will object
    trees with a much higher count. Some of the types accept abstract
    interfaces, and typeinfo must be maintained such that the library can
    correctly traverse the object tree. After construction of these object
    trees, the user will pass them to another library object, and be done
    with it.

    Is it best to:

    a. Use pointers, and delete pointers automatically in each class
    destructor (freeing user from deleting them but preventing them from
    keeping a reference)

    b. Use pointers, and require user to delete pointers manually (thus
    killing the ability to chain the construction since a pointer to each
    object now needs to be kept)

    c. Use references - though I'm not sure how that would work with
    abstract types...how would you pass an inline contructed derived class
    reference where the abstract class reference is expected?

    My inclination is to use choice a, though I know pointers are not type
    safe, and I'm not sure how good of an idea it is to automatically delete
    all the objects in the tree.

    Thoughts?

    Thanks in advance.
    Brian Buderman, Dec 18, 2006
    #1
    1. Advertising

  2. Brian Buderman

    noone Guest

    On Sun, 17 Dec 2006 23:18:25 -0500, Brian Buderman wrote:

    > Consider the creation of a class library, where the desire is to allow the
    > user of the library to chain objects together during construction in
    > a single statement. For example, this statement uses the heap:
    >
    > topLevelObject = new type1(new type2(new type3(), new type4());


    Cplusplus != JAVA

    I wouldn't consider this to be good form in C++. It unnecessarily
    complicates the code.

    I'd design my objects to have small sizeof() values and let the expression
    be evaluated on the stack, using copy constructors to supply the
    intermediate object values needed.

    However, it is 2AM here so I may reconsider later, but right now I don't
    think what you are proposing is a good idea.
    noone, Dec 18, 2006
    #2
    1. Advertising

  3. Brian Buderman

    Alan Johnson Guest

    Brian Buderman wrote:
    > Consider the creation of a class library, where the desire is to allow
    > the user of the library to chain objects together during construction in
    > a single statement. For example, this statement uses the heap:
    >
    > topLevelObject = new type1(new type2(new type3(), new type4());


    This is not safe. Consider what happens if the expression "new type3()"
    is evaluated, and then an exception is thrown during the evaluation of
    "new type4()". (Hint: memory leak)

    >
    > While this example only uses 4 types, in actuality there will object
    > trees with a much higher count. Some of the types accept abstract
    > interfaces, and typeinfo must be maintained such that the library can
    > correctly traverse the object tree. After construction of these object
    > trees, the user will pass them to another library object, and be done
    > with it.
    >


    Instead of carrying type information along with it, I suggest looking up
    the Visitor pattern. It was conceived to address this recurring problem
    of trying to traverse and perform actions on the nodes of a data
    structure where the nodes are of varying type.

    As for storage, I suggest you make use of auto_ptr. Accept the objects
    as auto_ptrs and store them internally that way as well. Be sure you
    either disallow or properly implement a copy constructor and operator=.
    This can also help with your exception safety problem above. For each
    type, add a static function that creates an object of that type and
    returns an auto_ptr. Example:

    class type3
    {
    public:
    // Note: this can take parameters as necessary.
    static std::auto_ptr<type3> create() { return new type3() ; }
    } ;

    Then your tree would be built with something like:

    std::auto_ptr<type1> topLevelObject =
    type1::create(type2::create(type3::create(), type4::create())) ;

    Now, by the time "type3::create()" is evaluated, the memory that was
    allocated is already being managed by an auto_ptr, so if
    "type4::create()" throws an exception, it will be properly destructed.

    Hope this helps.

    --
    Alan Johnson
    Alan Johnson, Dec 18, 2006
    #3
  4. Brian Buderman

    Alan Johnson Guest

    Alan Johnson wrote:
    > As for storage, I suggest you make use of auto_ptr. Accept the objects
    > as auto_ptrs and store them internally that way as well. Be sure you
    > either disallow or properly implement a copy constructor and operator=.
    > This can also help with your exception safety problem above. For each
    > type, add a static function that creates an object of that type and
    > returns an auto_ptr. Example:
    >
    > class type3
    > {
    > public:
    > // Note: this can take parameters as necessary.
    > static std::auto_ptr<type3> create() { return new type3() ; }
    > } ;
    >
    > Then your tree would be built with something like:
    >
    > std::auto_ptr<type1> topLevelObject =
    > type1::create(type2::create(type3::create(), type4::create())) ;


    I forgot to take into account the pain-in-the-assness of auto_ptr when I
    suggested this. Since the return value, being temporary, can't bind to
    the non-const reference in auto_ptr's constructor, that last line won't
    work. So either you'd have to construct it piecemeal:
    std::auto_ptr<base> t3(type3::create()) ;
    std::auto_ptr<base> t4(type4::create()) ;
    std::auto_ptr<base> t2(type2::create(t3, t4)) ;
    std::auto_ptr<base> t1(type1::create(t2)) ;

    or use some other smart pointer like tr1::shared_ptr (or
    boost::shared_ptr if your implementation doesn't have tr1).

    --
    Alan Johnson
    Alan Johnson, Dec 18, 2006
    #4
  5. Brian Buderman

    Daniel T. Guest

    Brian Buderman <> wrote:

    > Consider the creation of a class library, where the desire is to allow
    > the user of the library to chain objects together during construction in
    > a single statement. For example, this statement uses the heap:
    >
    > topLevelObject = new type1(new type2(new type3(), new type4());
    >
    > While this example only uses 4 types, in actuality there will object
    > trees with a much higher count. Some of the types accept abstract
    > interfaces, and typeinfo must be maintained such that the library can
    > correctly traverse the object tree. After construction of these object
    > trees, the user will pass them to another library object, and be done
    > with it.
    >
    > Is it best to:
    >
    > a. Use pointers, and delete pointers automatically in each class
    > destructor (freeing user from deleting them but preventing them from
    > keeping a reference)
    >
    > b. Use pointers, and require user to delete pointers manually (thus
    > killing the ability to chain the construction since a pointer to each
    > object now needs to be kept)
    >
    > c. Use references - though I'm not sure how that would work with
    > abstract types...how would you pass an inline contructed derived class
    > reference where the abstract class reference is expected?
    >
    > My inclination is to use choice a, though I know pointers are not type
    > safe, and I'm not sure how good of an idea it is to automatically delete
    > all the objects in the tree.
    >
    > Thoughts?


    This is strictly an issue of ownership. IMHO, the last object to send a
    message to an object should also be the one that destroys it. If the
    destroyer is not the same as the creator, then you must use heap ram. As
    such, I think (a) above is probably the best choice.

    However, there is another school of thought that is equally valid. To
    them, the object that creates an object should also be the one that
    deletes the object. There is much merit to this idea. It makes memory
    management easer in general (though it does make factory classes a
    little harder to implement,) and it allows the top level objects to
    decide whether heap or stack ram should be used for a particular object.
    The only reason I don't like such a scheme is that it requires the
    top-level objects to hold many objects that they created but never use
    themselves.

    BTW, pointers are type safe.
    Daniel T., Dec 18, 2006
    #5
  6. Brian Buderman

    Mirek Fidler Guest

    Brian Buderman wrote:
    > Consider the creation of a class library, where the desire is to allow
    > the user of the library to chain objects together during construction in
    > a single statement. For example, this statement uses the heap:
    >
    > topLevelObject = new type1(new type2(new type3(), new type4());
    >
    > While this example only uses 4 types, in actuality there will object
    > trees with a much higher count. Some of the types accept abstract
    > interfaces, and typeinfo must be maintained such that the library can
    > correctly traverse the object tree. After construction of these object
    > trees, the user will pass them to another library object, and be done
    > with it.
    >
    > Is it best to:
    >
    > a. Use pointers, and delete pointers automatically in each class
    > destructor (freeing user from deleting them but preventing them from
    > keeping a reference)
    >
    > b. Use pointers, and require user to delete pointers manually (thus
    > killing the ability to chain the construction since a pointer to each
    > object now needs to be kept)
    >
    > c. Use references - though I'm not sure how that would work with
    > abstract types...how would you pass an inline contructed derived class
    > reference where the abstract class reference is expected?
    >
    > Thoughts?


    What about "create inside" interface:

    Type1 x;
    Type2& t1 = x.CreateType2();
    Type3& t2 = t1.CreateType3();
    ......

    and manage resources inside Type1, Type2, Type3... (e.g. use owning
    containers to implement them).

    Mirek
    Mirek Fidler, Dec 18, 2006
    #6
  7. Brian Buderman

    Daniel T. Guest

    Alan Johnson <> wrote:
    > Brian Buderman wrote:


    > > Consider the creation of a class library, where the desire is to allow
    > > the user of the library to chain objects together during construction in
    > > a single statement. For example, this statement uses the heap:
    > >
    > > topLevelObject = new type1(new type2(new type3(), new type4());

    >
    > This is not safe. Consider what happens if the expression "new type3()"
    > is evaluated, and then an exception is thrown during the evaluation of
    > "new type4()". (Hint: memory leak)
    >
    > >
    > > While this example only uses 4 types, in actuality there will object
    > > trees with a much higher count. Some of the types accept abstract
    > > interfaces, and typeinfo must be maintained such that the library can
    > > correctly traverse the object tree. After construction of these object
    > > trees, the user will pass them to another library object, and be done
    > > with it.
    > >

    >
    > Instead of carrying type information along with it, I suggest looking up
    > the Visitor pattern. It was conceived to address this recurring problem
    > of trying to traverse and perform actions on the nodes of a data
    > structure where the nodes are of varying type.
    >
    > As for storage, I suggest you make use of auto_ptr. Accept the objects
    > as auto_ptrs and store them internally that way as well. Be sure you
    > either disallow or properly implement a copy constructor and operator=.
    > This can also help with your exception safety problem above. For each
    > type, add a static function that creates an object of that type and
    > returns an auto_ptr. Example:
    >
    > class type3
    > {
    > public:
    > // Note: this can take parameters as necessary.
    > static std::auto_ptr<type3> create() { return new type3() ; }
    > } ;
    >
    > Then your tree would be built with something like:
    >
    > std::auto_ptr<type1> topLevelObject =
    > type1::create(type2::create(type3::create(), type4::create())) ;
    >
    > Now, by the time "type3::create()" is evaluated, the memory that was
    > allocated is already being managed by an auto_ptr, so if
    > "type4::create()" throws an exception, it will be properly destructed.
    >
    > Hope this helps.


    Rather than all that, how about simply using the nothrow version of new
    in the OP's line above.
    Daniel T., Dec 18, 2006
    #7
  8. On 18 Dec 2006 04:06:39 -0800, "Mirek Fidler" wrote:
    >What about "create inside" interface:
    >
    >Type1 x;
    >Type2& t1 = x.CreateType2();
    >Type3& t2 = t1.CreateType3();
    >.....
    >and manage resources inside Type1, Type2, Type3... (e.g. use owning
    >containers to implement them).


    Better known as 'Creator As Sole Owner' pattern:
    http://www.ddj.com/184409895

    Best wishes,
    Roland Pibinger
    Roland Pibinger, Dec 18, 2006
    #8
  9. Brian Buderman

    Mirek Fidler Guest

    Roland Pibinger wrote:
    > On 18 Dec 2006 04:06:39 -0800, "Mirek Fidler" wrote:
    > >What about "create inside" interface:
    > >
    > >Type1 x;
    > >Type2& t1 = x.CreateType2();
    > >Type3& t2 = t1.CreateType3();
    > >.....
    > >and manage resources inside Type1, Type2, Type3... (e.g. use owning
    > >containers to implement them).

    >
    > Better known as 'Creator As Sole Owner' pattern:
    > http://www.ddj.com/184409895
    >


    Thanks.

    Mirek
    Mirek Fidler, Dec 18, 2006
    #9
  10. Brian Buderman

    Alan Johnson Guest

    Daniel T. wrote:
    > Alan Johnson <> wrote:
    >> Brian Buderman wrote:

    >
    >>> Consider the creation of a class library, where the desire is to allow
    >>> the user of the library to chain objects together during construction in
    >>> a single statement. For example, this statement uses the heap:
    >>>
    >>> topLevelObject = new type1(new type2(new type3(), new type4());

    >> This is not safe. Consider what happens if the expression "new type3()"
    >> is evaluated, and then an exception is thrown during the evaluation of
    >> "new type4()". (Hint: memory leak)
    >>
    >>> While this example only uses 4 types, in actuality there will object
    >>> trees with a much higher count. Some of the types accept abstract
    >>> interfaces, and typeinfo must be maintained such that the library can
    >>> correctly traverse the object tree. After construction of these object
    >>> trees, the user will pass them to another library object, and be done
    >>> with it.
    >>>

    >> Instead of carrying type information along with it, I suggest looking up
    >> the Visitor pattern. It was conceived to address this recurring problem
    >> of trying to traverse and perform actions on the nodes of a data
    >> structure where the nodes are of varying type.
    >>
    >> As for storage, I suggest you make use of auto_ptr. Accept the objects
    >> as auto_ptrs and store them internally that way as well. Be sure you
    >> either disallow or properly implement a copy constructor and operator=.
    >> This can also help with your exception safety problem above. For each
    >> type, add a static function that creates an object of that type and
    >> returns an auto_ptr. Example:
    >>
    >> class type3
    >> {
    >> public:
    >> // Note: this can take parameters as necessary.
    >> static std::auto_ptr<type3> create() { return new type3() ; }
    >> } ;
    >>
    >> Then your tree would be built with something like:
    >>
    >> std::auto_ptr<type1> topLevelObject =
    >> type1::create(type2::create(type3::create(), type4::create())) ;
    >>
    >> Now, by the time "type3::create()" is evaluated, the memory that was
    >> allocated is already being managed by an auto_ptr, so if
    >> "type4::create()" throws an exception, it will be properly destructed.
    >>
    >> Hope this helps.

    >
    > Rather than all that, how about simply using the nothrow version of new
    > in the OP's line above.


    Because nothrow is a false promise. It does not guarantee that no
    exceptions will be thrown, only that operator new will not throw
    bad_alloc. The constructor of the object being allocated may still
    throw exceptions.

    --
    Alan Johnson
    Alan Johnson, Dec 18, 2006
    #10
    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. joe

    stack and heap question

    joe, Aug 4, 2004, in forum: Java
    Replies:
    3
    Views:
    429
    Michael Borgwardt
    Aug 5, 2004
  2. Steve Hill

    vector.swap and heap / stack

    Steve Hill, Aug 20, 2003, in forum: C++
    Replies:
    5
    Views:
    996
    tom_usenet
    Aug 22, 2003
  3. Michal Slocinski

    Heap dump file size vs heap size

    Michal Slocinski, Mar 25, 2008, in forum: Java
    Replies:
    1
    Views:
    731
    GArlington
    Mar 25, 2008
  4. viki
    Replies:
    6
    Views:
    558
    Erik Wikström
    Jun 28, 2008
  5. Raymond Schanks
    Replies:
    0
    Views:
    516
    Raymond Schanks
    Apr 11, 2010
Loading...

Share This Page