Convention for Object Lifetime Requirement

Discussion in 'C++' started by Belebele, Mar 29, 2006.

  1. Belebele

    Belebele Guest

    Suppose that a class A depends on class B because an object of class B
    is passed into A's constructor. See below:

    class B { ... };

    class A {
    public:
    A(B const& b);
    ...
    };

    Does anybody use any convention (comment, code, etc) to indicate
    whether or not the object of class A requires b to exist beyond the
    call to A's constructor?

    For example, in the case of copy constructors, I would find odd to
    learn that for objects o1 and o2 of certain class O, where o1 was
    constructed from o2, o1 requires o2 to exist beyond the call to o1's
    constructor.

    I would like to extend the discussion to other types of dependencies,
    for example, when A declares a member function that takes an object of
    class B as a parameter.

    Thanks:

    Belebele
    Belebele, Mar 29, 2006
    #1
    1. Advertising

  2. On 29 Mar 2006 11:23:30 -0800, "Belebele" <> wrote:
    >Suppose that a class A depends on class B because an object of class B
    >is passed into A's constructor. See below:
    >
    >class B { ... };
    >
    >class A {
    >public:
    > A(B const& b);
    > ...
    >};
    >
    >Does anybody use any convention (comment, code, etc) to indicate
    >whether or not the object of class A requires b to exist beyond the
    >call to A's constructor?


    No. This setting seems to be an anti-idiom. When A depends on B then
    let A create B in the constructor:

    class A {
    B b;
    public:
    A (int i) : b(i) {}
    ...
    };

    >For example, in the case of copy constructors, I would find odd to
    >learn that for objects o1 and o2 of certain class O, where o1 was
    >constructed from o2, o1 requires o2 to exist beyond the call to o1's
    >constructor.


    In that case you can only prevent copy construction and assignment.

    >I would like to extend the discussion to other types of dependencies,
    >for example, when A declares a member function that takes an object of
    >class B as a parameter.


    References to 'outside' objects also violates encapsulation. If A
    really needs B then pass a B object as argument to the function, eg.

    class A {
    // as above
    void foo (const B& b, float f) { ... }
    ...
    };

    Best wishes,
    Roland Pibinger
    Roland Pibinger, Mar 29, 2006
    #2
    1. Advertising

  3. Belebele

    mlimber Guest

    Belebele wrote:
    > Suppose that a class A depends on class B because an object of class B
    > is passed into A's constructor. See below:
    >
    > class B { ... };
    >
    > class A {
    > public:
    > A(B const& b);
    > ...
    > };
    >
    > Does anybody use any convention (comment, code, etc) to indicate
    > whether or not the object of class A requires b to exist beyond the
    > call to A's constructor?


    Yes, use smart pointers instead of raw pointers or references when
    there is some special relationship. For instance, if A takes over
    ownership of a B object, pass a std::auto_ptr<B> into A's constructor,
    or if A doesn't uniquely own that B object but needs it to exist after
    the constructor has completed, accept a std::tr1::shared_ptr<B> (or
    boost::shared_ptr<B>) or similar and store it as a member of A.

    > For example, in the case of copy constructors, I would find odd to
    > learn that for objects o1 and o2 of certain class O, where o1 was
    > constructed from o2, o1 requires o2 to exist beyond the call to o1's
    > constructor.
    >
    > I would like to extend the discussion to other types of dependencies,
    > for example, when A declares a member function that takes an object of
    > class B as a parameter.


    The same idioms apply in these two cases, methinks.

    Cheers! --M
    mlimber, Mar 29, 2006
    #3
  4. Belebele

    Phlip Guest

    Roland Pibinger wrote:

    > No. This setting seems to be an anti-idiom. When A depends on B then
    > let A create B in the constructor:
    >
    > class A {
    > B b;
    > public:
    > A (int i) : b(i) {}
    > ...
    > };


    Google for "construction encapsulation".

    (And it's heartwarming to see how, each year, this newsgroup gets posts
    detracting from the good memes of yesteryear as if they were "myths", or
    "anti-idioms"...)

    --
    Phlip
    http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
    Phlip, Mar 29, 2006
    #4
  5. Belebele

    Axter Guest

    mlimber wrote:
    > Belebele wrote:
    > > Suppose that a class A depends on class B because an object of class B
    > > is passed into A's constructor. See below:
    > >
    > > class B { ... };
    > >
    > > class A {
    > > public:
    > > A(B const& b);
    > > ...
    > > };
    > >
    > > Does anybody use any convention (comment, code, etc) to indicate
    > > whether or not the object of class A requires b to exist beyond the
    > > call to A's constructor?

    >
    > Yes, use smart pointers instead of raw pointers or references when
    > there is some special relationship. For instance, if A takes over
    > ownership of a B object, pass a std::auto_ptr<B> into A's constructor,
    > or if A doesn't uniquely own that B object but needs it to exist after
    > the constructor has completed, accept a std::tr1::shared_ptr<B> (or
    > boost::shared_ptr<B>) or similar and store it as a member of A.
    >
    > > For example, in the case of copy constructors, I would find odd to
    > > learn that for objects o1 and o2 of certain class O, where o1 was
    > > constructed from o2, o1 requires o2 to exist beyond the call to o1's
    > > constructor.
    > >
    > > I would like to extend the discussion to other types of dependencies,
    > > for example, when A declares a member function that takes an object of
    > > class B as a parameter.

    >
    > The same idioms apply in these two cases, methinks.
    >


    If you use a reference counting smart pointer, be careful not to add
    cycles to your code, because the cyclic dependency prevents refcounts
    from ever going to
    zero, and therefore leaves memory leaks in your code.

    Also see alternative to boost::shared_ptr
    http://axter.com/smartptr

    ----------------------------------------------------------------------------------------
    David Maisonave
    http://axter.com

    Top ten member of C++ Expert Exchange:
    http://www.experts-exchange.com/Cplusplus
    ----------------------------------------------------------------------------------------
    Axter, Mar 29, 2006
    #5
  6. Belebele

    Daniel T. Guest

    In article <>,
    "Belebele" <> wrote:

    > Suppose that a class A depends on class B because an object of class B
    > is passed into A's constructor. See below:
    >
    > class B { ... };
    >
    > class A {
    > public:
    > A(B const& b);
    > ...
    > };
    >
    > Does anybody use any convention (comment, code, etc) to indicate
    > whether or not the object of class A requires b to exist beyond the
    > call to A's constructor?


    A couple people have said "use a smart pointer" one even specified
    shared_ptr. I think this is a bad idea in general. For one thing, there
    is no guarantee that the B object passed in was created on the heap, it
    might be stack based for all A knows. For another, if B is sending
    itself to A, (and is also wrapped in a shared_ptr by some other means,
    it can't possibly be good for it to send 'this' to A's constructor.


    > For example, in the case of copy constructors, I would find odd to
    > learn that for objects o1 and o2 of certain class O, where o1 was
    > constructed from o2, o1 requires o2 to exist beyond the call to o1's
    > constructor.
    >
    > I would like to extend the discussion to other types of dependencies,
    > for example, when A declares a member function that takes an object of
    > class B as a parameter.


    The general rule I use is that if the parameter is passed by reference
    then the parameter is not going to be stored. The caller is free to
    destroy the parameter object at anytime after the function returns. If
    the parameter is passed in by pointer then it may be stored, check the
    documentation to see how long (either until the end of the function,
    until certain non-const member-functions are called, or until object
    destruction.)



    --
    Magic depends on tradition and belief. It does not welcome observation,
    nor does it profit by experiment. On the other hand, science is based
    on experience; it is open to correction by observation and experiment.
    Daniel T., Mar 30, 2006
    #6
  7. Belebele

    mlimber Guest

    Daniel T. wrote:
    > In article <>,
    > "Belebele" <> wrote:
    >
    > > Suppose that a class A depends on class B because an object of class B
    > > is passed into A's constructor. See below:
    > >
    > > class B { ... };
    > >
    > > class A {
    > > public:
    > > A(B const& b);
    > > ...
    > > };
    > >
    > > Does anybody use any convention (comment, code, etc) to indicate
    > > whether or not the object of class A requires b to exist beyond the
    > > call to A's constructor?

    >
    > A couple people have said "use a smart pointer" one even specified
    > shared_ptr. I think this is a bad idea in general. For one thing, there
    > is no guarantee that the B object passed in was created on the heap, it
    > might be stack based for all A knows.


    You are right that you couldn't pass an automatic object -- that's part
    of the trade-off for using this idiom. The implicit requirement of
    using a smart pointer as a constructor parameter is that the B object
    be created in such as way as to be compatible with using that smart
    pointer, and since the deleter of some (e.g., boost::shared_ptr) can be
    specified, the object could be created by various methods. Also, in the
    case that B is non-copyable but A needs to retain an instance of it, a
    (smart) pointer of some kind is the only option.

    > For another, if B is sending
    > itself to A, (and is also wrapped in a shared_ptr by some other means,
    > it can't possibly be good for it to send 'this' to A's constructor.


    This is true, but there are ways around it, not least of which is
    considering different designs. What we're concerned with here is making
    sure that an A's stored reference to a B object doesn't dangle or leak.
    Passing a raw pointer/reference gives the most flexibility but
    consequently (1) communicates the least about A's intentions toward
    that B object and (2) allows a greater number of errors. Using the
    smart pointer idiom clarifies A's intentions by encoding and enforcing
    the life time expectations for the B object by making explicit who is
    responsibile for destroying B and when it should be done.

    > > For example, in the case of copy constructors, I would find odd to
    > > learn that for objects o1 and o2 of certain class O, where o1 was
    > > constructed from o2, o1 requires o2 to exist beyond the call to o1's
    > > constructor.
    > >
    > > I would like to extend the discussion to other types of dependencies,
    > > for example, when A declares a member function that takes an object of
    > > class B as a parameter.

    >
    > The general rule I use is that if the parameter is passed by reference
    > then the parameter is not going to be stored. The caller is free to
    > destroy the parameter object at anytime after the function returns. If
    > the parameter is passed in by pointer then it may be stored, check the
    > documentation to see how long (either until the end of the function,
    > until certain non-const member-functions are called, or until object
    > destruction.)


    This is a commendable convention, however, who is to say that the user
    of your code (or the programmer who maintains it in several years) will
    follow it? Using the smart pointer idiom enforces the life time
    management requirements that are required by A in a way that your
    convention and personal discipline cannot.

    Cheers! --M
    mlimber, Mar 30, 2006
    #7
  8. Belebele

    mlimber Guest

    mlimber, Mar 30, 2006
    #8
  9. Belebele

    Daniel T. Guest

    In article <>,
    "mlimber" <> wrote:

    > Daniel T. wrote:
    > > In article <>,
    > > "Belebele" <> wrote:
    > >
    > > > Suppose that a class A depends on class B because an object of class B
    > > > is passed into A's constructor. See below:
    > > >
    > > > class B { ... };
    > > >
    > > > class A {
    > > > public:
    > > > A(B const& b);
    > > > ...
    > > > };
    > > >
    > > > Does anybody use any convention (comment, code, etc) to indicate
    > > > whether or not the object of class A requires b to exist beyond the
    > > > call to A's constructor?

    > >
    > > A couple people have said "use a smart pointer" one even specified
    > > shared_ptr. I think this is a bad idea in general. For one thing, there
    > > is no guarantee that the B object passed in was created on the heap, it
    > > might be stack based for all A knows.

    >
    > You are right that you couldn't pass an automatic object -- that's part
    > of the trade-off for using this idiom. The implicit requirement of
    > using a smart pointer as a constructor parameter is that the B object
    > be created in such as way as to be compatible with using that smart
    > pointer, and since the deleter of some (e.g., boost::shared_ptr) can be
    > specified, the object could be created by various methods. Also, in the
    > case that B is non-copyable but A needs to retain an instance of it, a
    > (smart) pointer of some kind is the only option.
    >
    > > For another, if B is sending
    > > itself to A, (and is also wrapped in a shared_ptr by some other means,
    > > it can't possibly be good for it to send 'this' to A's constructor.

    >
    > This is true, but there are ways around it, not least of which is
    > considering different designs. What we're concerned with here is making
    > sure that an A's stored reference to a B object doesn't dangle or leak.
    > Passing a raw pointer/reference gives the most flexibility but
    > consequently (1) communicates the least about A's intentions toward
    > that B object and (2) allows a greater number of errors. Using the
    > smart pointer idiom clarifies A's intentions by encoding and enforcing
    > the life time expectations for the B object by making explicit who is
    > responsibile for destroying B and when it should be done.
    >
    > > > For example, in the case of copy constructors, I would find odd to
    > > > learn that for objects o1 and o2 of certain class O, where o1 was
    > > > constructed from o2, o1 requires o2 to exist beyond the call to o1's
    > > > constructor.
    > > >
    > > > I would like to extend the discussion to other types of dependencies,
    > > > for example, when A declares a member function that takes an object of
    > > > class B as a parameter.

    > >
    > > The general rule I use is that if the parameter is passed by reference
    > > then the parameter is not going to be stored. The caller is free to
    > > destroy the parameter object at anytime after the function returns. If
    > > the parameter is passed in by pointer then it may be stored, check the
    > > documentation to see how long (either until the end of the function,
    > > until certain non-const member-functions are called, or until object
    > > destruction.)

    >
    > This is a commendable convention, however, who is to say that the user
    > of your code (or the programmer who maintains it in several years) will
    > follow it? Using the smart pointer idiom enforces the life time
    > management requirements that are required by A in a way that your
    > convention and personal discipline cannot.


    My convention isn't the only one that requires discipline. Use of a
    smart pointer also requires discipline. When an object is placed in a
    smart pointer, one must ensure that (a) the object was dynamically
    allocated, (b) that the smart pointer knows how to properly deallocate
    it and (c) no other pointer to that object exists either as a raw
    pointer, or within another smart pointer type.

    So given, for example, two libraries that I want to use which use two
    different smart pointers, what benefit do they give me when I want to
    share an object between them? If anything my plight is worse than if
    they (or at least one of them) used raw pointers.

    Please understand I think smart pointers are a great idea, they are used
    to excellent effect in some languages where every object is held by one.
    But unless and until one smart pointer class holds every dynamically
    allocated object no matter what library creates it, and cannot be
    accidentally made to hold objects that were not dynamically allocated,
    use of the smart pointer requires no less discipline, nor is it any less
    error prone than use of a raw pointer.



    --
    Magic depends on tradition and belief. It does not welcome observation,
    nor does it profit by experiment. On the other hand, science is based
    on experience; it is open to correction by observation and experiment.
    Daniel T., Mar 31, 2006
    #9
  10. Belebele

    mlimber Guest

    Daniel T. wrote:
    > "mlimber" <> wrote:
    > > Daniel T. wrote:
    > > > The general rule I use is that if the parameter is passed by reference
    > > > then the parameter is not going to be stored. The caller is free to
    > > > destroy the parameter object at anytime after the function returns. If
    > > > the parameter is passed in by pointer then it may be stored, check the
    > > > documentation to see how long (either until the end of the function,
    > > > until certain non-const member-functions are called, or until object
    > > > destruction.)

    > >
    > > This is a commendable convention, however, who is to say that the user
    > > of your code (or the programmer who maintains it in several years) will
    > > follow it? Using the smart pointer idiom enforces the life time
    > > management requirements that are required by A in a way that your
    > > convention and personal discipline cannot.

    >
    > My convention isn't the only one that requires discipline. Use of a
    > smart pointer also requires discipline. When an object is placed in a
    > smart pointer, one must ensure that (a) the object was dynamically
    > allocated, (b) that the smart pointer knows how to properly deallocate
    > it and (c) no other pointer to that object exists either as a raw
    > pointer, or within another smart pointer type.


    Of course you are right that different discipline is also required for
    proper smart pointer usage. My point (perhaps poorly communicated) was
    primarily that *if* the smart pointer is used properly (just as it is
    expected that one will not pass a dangling reference/pointer into
    A::A() in your convention), then passing one to or returning one from a
    function clearly communicates and enforces expectations for the
    life-time management of that object. Use of smart pointers are
    certainly not fool-proof, but using them across the board generally
    makes things simpler and safer (especially when exceptions come into
    play).

    Compared to this explicitness, using raw pointers/references
    necessitates nothing about life-time management since one can use them
    "properly" in a number of ways -- your excellent convention being just
    one of those. Smart pointers have (relatively) clearly defined rules
    and expectations; raw pointers do not. Your convention may exist in
    your personal or your company's coding standards, the written
    documentation, or code comments, but that makes it more dependent on
    the people involved and their discipline. Making life-time management
    instead depend on the language rules via RAII and smart pointers makes
    code more robust.

    > So given, for example, two libraries that I want to use which use two
    > different smart pointers, what benefit do they give me when I want to
    > share an object between them? If anything my plight is worse than if
    > they (or at least one of them) used raw pointers.


    Well, if one uses std::auto_ptr and the other uses
    std::tr1::shared_ptr, obviously they are incompatible because of
    differing requirements, and such a case would be incompatible in your
    convention as well, though I would hasten to point out that, unlike
    with smart pointers, this incompatibility would not be clear just from
    the interface.

    If one uses std::tr1::shared_ptr and the other a roughly equivalent
    reference-counted smart pointer (e.g., Loki::SmartPtr), then you do
    have a problem, though you might be able to resolve it by supplying
    compatible policies (for Loki::SmartPtr) or with a partial
    specialization of the smart pointer class (for some other
    implementation). ISTM that that problem will be mitigated somewhat
    if/when shared_ptr becomes an official part of the standard. Then you
    could justly complain to the library vendor that they should use
    standard features rather than non-standard but equivalent ones.

    In my experience, most third-party libraries today don't offer smart
    pointers in their interfaces mainly because of the non-standard nature
    of the beasts. Hopefully, that will change for the better if/when the
    standardization includes more smart pointers, but for the time being,
    one can use smart pointers internally in the project and convert them
    to raw pointers when sending to third-party libraries (cf. using
    std::vector with C APIs).

    > Please understand I think smart pointers are a great idea, they are used
    > to excellent effect in some languages where every object is held by one.
    > But unless and until one smart pointer class holds every dynamically
    > allocated object no matter what library creates it, and cannot be
    > accidentally made to hold objects that were not dynamically allocated,
    > use of the smart pointer requires no less discipline, nor is it any less
    > error prone than use of a raw pointer.


    I agree fully that the discipline required for using smart pointers is
    different, but I would argue that their use is indeed less error prone,
    especially in the face of exceptions. The same thing is true of using
    std::vector instead of C-style arrays: though the former requires
    different discipline for proper usage (e.g., making sure iterators are
    not invalidated), its benefits -- clearly defined life-time management,
    exception-safety, and elimination of common programmer errors (e.g.,
    delete instead of delete[]) -- out-weigh the downsides in most
    circumstances (cf. FAQ 34.1).

    Cheers! --M
    mlimber, Mar 31, 2006
    #10
  11. Belebele

    Belebele Guest

    mlimber wrote:

    > You are right that you couldn't pass an automatic object -- that's part
    > of the trade-off for using this idiom. The implicit requirement of
    > using a smart pointer as a constructor parameter is that the B object
    > be created in such as way as to be compatible with using that smart
    > pointer, and since the deleter of some (e.g., boost::shared_ptr) can be
    > specified, the object could be created by various methods.


    I did not know such feature (the deleter policy for the smart pointer)
    existed in some standard way (that is, on a library somewhere). It
    occurs to me that it can be put to good use in case the B object is not
    in a smart pointer form. If the client of A knows that the B object is
    guaranteed to meet the lifetime requirements imposed by A, it can
    create a smart pointer to B with a deleter that does nothing and pass
    it to A.

    > This is a commendable convention, however, who is to say that the user
    > of your code (or the programmer who maintains it in several years) will
    > follow it?


    Enough said. However, if we do not know better (or are not fully
    comfortable with the alternatives), the comment convention is, well,
    highly commendable. (I will stick to using references as parameters if
    clients are required to provide the B object, and pointers if the B
    object may or may not be provided)

    > Cheers! --M
    Belebele, Apr 1, 2006
    #11
  12. On Wed, 29 Mar 2006 19:46:30 GMT, (Roland Pibinger)
    wrote:
    >On 29 Mar 2006 11:23:30 -0800, "Belebele" <> wrote:
    >>Suppose that a class A depends on class B because an object of class B
    >>is passed into A's constructor. See below:
    >>
    >>class B { ... };
    >>
    >>class A {
    >>public:
    >> A(B const& b);
    >> ...
    >>};
    >>
    >>Does anybody use any convention (comment, code, etc) to indicate
    >>whether or not the object of class A requires b to exist beyond the
    >>call to A's constructor?

    >
    >No. This setting seems to be an anti-idiom. When A depends on B then
    >let A create B in the constructor:


    Actually, there is an idiom where the above code makes sense:
    dependency inversion, e.g.

    Connection conn (....);
    Database db (conn);
    Table mytable (db);
    MyObject* pObj = mytable.readByPrimaryKey (123);

    To prevent errors (or make them less probable) all objects in the
    'dependeny inversion chain' should be non-copyable/non-assignable.

    Best wishes,
    Roland Pibinger
    Roland Pibinger, Apr 9, 2006
    #12
  13. Belebele

    mlimber Guest

    mlimber wrote:
    > Daniel T. wrote:
    > > A couple people have said "use a smart pointer" one even specified
    > > shared_ptr. I think this is a bad idea in general. For one thing, there
    > > is no guarantee that the B object passed in was created on the heap, it
    > > might be stack based for all A knows.

    >
    > You are right that you couldn't pass an automatic object -- that's part
    > of the trade-off for using this idiom. The implicit requirement of
    > using a smart pointer as a constructor parameter is that the B object
    > be created in such as way as to be compatible with using that smart
    > pointer, and since the deleter of some (e.g., boost::shared_ptr) can be
    > specified, the object could be created by various methods. Also, in the
    > case that B is non-copyable but A needs to retain an instance of it, a
    > (smart) pointer of some kind is the only option.
    >
    > > For another, if B is sending
    > > itself to A, (and is also wrapped in a shared_ptr by some other means,
    > > it can't possibly be good for it to send 'this' to A's constructor.

    >
    > This is true, but there are ways around it, not least of which is
    > considering different designs. What we're concerned with here is making
    > sure that an A's stored reference to a B object doesn't dangle or leak.
    > Passing a raw pointer/reference gives the most flexibility but
    > consequently (1) communicates the least about A's intentions toward
    > that B object and (2) allows a greater number of errors. Using the
    > smart pointer idiom clarifies A's intentions by encoding and enforcing
    > the life time expectations for the B object by making explicit who is
    > responsibile for destroying B and when it should be done.


    I retract both of these concessions to Daniel T.

    First, using a custom deleter (in particular, a deleter that does
    nothing), one certainly can send an object allocated on the stack to a
    function expecting a smart pointer such as std::tr1::shared_ptr. (One
    might worry that the function might try to retain a copy of the pointer
    to the object, which will dangle when the automatic object goes out of
    scope. This is true, but it equally applies to raw pointers.)

    Second, std::tr1::shared_ptr certainly can point to "this" by
    inheriting from std::tr1::enable_shared_from_this. (Alternately, one
    can use a boost::intrusive_ptr, though this is not part of TR1 and is
    not preferred except for legacy code.)

    For more details, see the online docs or chapter 1 of Bjorn Karlsson's
    _Beyond the C++ Standard Library: An Introduction to Boost_.

    Cheers! --M
    mlimber, Apr 21, 2006
    #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. Colin Basterfield

    lifetime of Session object

    Colin Basterfield, Jun 2, 2004, in forum: ASP .Net
    Replies:
    2
    Views:
    429
    Colin Basterfield
    Jun 3, 2004
  2. Replies:
    0
    Views:
    402
  3. =?Utf-8?B?RGlmZmlkZW50?=

    Worker process and Cache object's lifetime

    =?Utf-8?B?RGlmZmlkZW50?=, Feb 2, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    450
    =?Utf-8?B?UGV0ZXIgQnJvbWJlcmcgW0MjIE1WUF0=?=
    Feb 2, 2006
  4. pt
    Replies:
    8
    Views:
    755
  5. Oliver Tengler

    SWIG: Tie lifetime of object to a result

    Oliver Tengler, Nov 14, 2003, in forum: Python
    Replies:
    0
    Views:
    273
    Oliver Tengler
    Nov 14, 2003
Loading...

Share This Page