Return object without exposing it to delete

Discussion in 'C++' started by Michael Pradel, Jan 26, 2007.

  1. Hi all,

    I just returned to C++ after programming in other languages, and find
    myself a bit puzzled about the following issue: (see attached code).

    How can I avoid the deletion of an object that is returned by a method?
    Is returning a clone of it the only possibility? That would use a lot of
    memory in case of larger objects.


    Thanks a lot!

    Michael


    #include <iostream>
    using namespace std;

    class Dummy {
    public:
    int n;
    };

    class Victim {
    private:
    Dummy *d;

    public:
    Victim() {
    d = new Dummy();
    d->n = 23;
    }

    Dummy* getDummy() {
    return d;
    }
    };

    int main(int argc, char **argv) {
    Victim *v = new Victim();
    Dummy *d = v->getDummy();

    cout << d->n << endl;
    delete d;
    cout << d->n << endl;

    return 0;
    }
    // EOF

    Output:
    23
    0
    Michael Pradel, Jan 26, 2007
    #1
    1. Advertising

  2. Michael Pradel

    Noah Roberts Guest

    Michael Pradel wrote:
    > Hi all,
    >
    > I just returned to C++ after programming in other languages, and find
    > myself a bit puzzled about the following issue: (see attached code).
    >
    > How can I avoid the deletion of an object that is returned by a method?
    > Is returning a clone of it the only possibility? That would use a lot of
    > memory in case of larger objects.


    You could use boost::shared_ptr with a nop deleter.

    void noop() {}

    shared_ptr<T> getT() const { return shared_ptr<T>(t, bind(&noop)); }

    However, I imagine this could cause problems if you where to finish
    using all the shared_ptrs that have deleters before this returned one
    went out of scope. Unfortunately shared_ptr is the only one that
    appears to let you override the deleter.
    Noah Roberts, Jan 26, 2007
    #2
    1. Advertising

  3. Michael Pradel

    Greg Guest

    On Jan 26, 8:51 am, Michael Pradel <> wrote:
    > I just returned to C++ after programming in other languages, and find
    > myself a bit puzzled about the following issue: >
    >
    > int main(int argc, char **argv) {
    > Victim *v = new Victim();
    > Dummy *d = v->getDummy();
    >
    > cout << d->n << endl;
    > delete d;
    > cout << d->n << endl;
    >
    > return 0;}// EOF
    >
    > How can I avoid the deletion of an object that is returned by a method?
    > Is returning a clone of it the only possibility? That would use a lot of
    > memory in case of larger objects.


    By simply observing a simple rule: the code responsible for allocating
    an object is also solely responsible for that object's deallocation. In
    this case, main() did not explicitly (or directly) allocate the Dummy
    object, "d", so main() should not be deleting "d".

    On the other hand, main() did allocate "v', so main() should delete v
    before it exits. Deleting v should cause v to delete d (since v was
    responsible for allocating d in the first place). The fact that the
    Victim class does not deallocate its d member should be considered a
    bug in the program.

    So by observing this simple convention regarding object deallocation,
    everyone ends up minding their own affairs and no one interferes with
    the affairs of anyone else. As a practical matter, though, a C++
    program should avoid explicit new and delete calls - and instead rely
    on shared_ptr's or auto_ptr's to manage object lifetimes on its behalf.

    Greg
    Greg, Jan 26, 2007
    #3
  4. Michael Pradel

    Daniel T. Guest

    Michael Pradel <> wrote:

    > I just returned to C++ after programming in other languages, and find
    > myself a bit puzzled about the following issue: (see attached code).
    >
    > How can I avoid the deletion of an object that is returned by a method?


    By documenting who is responsible for the object's deletion. The way I
    do that is by returning a reference when I don't want them to delete the
    object and a pointer when I do. (Yes they could still delete the object,
    but in the documentation I've told them not to do it, so they either
    know not to, or they are ignorant.)

    > Is returning a clone of it the only possibility?


    That or simply not giving them access to it.
    Daniel T., Jan 26, 2007
    #4
  5. Michael Pradel

    Daniel T. Guest

    In article <epdc6p$isp$>, Noah Roberts <>
    wrote:

    > Michael Pradel wrote:
    > > Hi all,
    > >
    > > I just returned to C++ after programming in other languages, and find
    > > myself a bit puzzled about the following issue: (see attached code).
    > >
    > > How can I avoid the deletion of an object that is returned by a method?
    > > Is returning a clone of it the only possibility? That would use a lot of
    > > memory in case of larger objects.

    >
    > You could use boost::shared_ptr with a nop deleter.
    >
    > void noop() {}
    >
    > shared_ptr<T> getT() const { return shared_ptr<T>(t, bind(&noop)); }


    delete &*getT();
    Daniel T., Jan 26, 2007
    #5
  6. Michael Pradel

    JLS Guest

    On Jan 26, 1:42 pm, "Daniel T." <> wrote:
    > In article <epdc6p$>, Noah Roberts <>
    > wrote:
    >
    > > Michael Pradel wrote:
    > > > Hi all,

    >
    > > > I just returned to C++ after programming in other languages, and find
    > > > myself a bit puzzled about the following issue: (see attached code).

    >
    > > > How can I avoid the deletion of an object that is returned by a method?
    > > > Is returning a clone of it the only possibility? That would use a lot of
    > > > memory in case of larger objects.

    >
    > > You could use boost::shared_ptr with a nop deleter.

    >
    > > void noop() {}

    >
    > > shared_ptr<T> getT() const { return shared_ptr<T>(t, bind(&noop)); }delete &*getT();


    What about returning a const ptr. Shouldn't that prevent the deletion?

    May be of limited usefullness, but it is one way of solving the
    problem. You could also make the destructor private and control who can
    delete the object.
    JLS, Jan 26, 2007
    #6
  7. > By simply observing a simple rule: the code responsible for allocating
    > an object is also solely responsible for that object's deallocation.


    That's a good solution for most of the cases, but e.g. using the Factory
    Method design pattern it doesn't make sense. Here one class is only
    responsible for creating the object while another will use it and knowns
    when to delete it.

    Product* Factory::create(ProductID id) {
    if (id == A) return new ProductA;
    // ...
    }

    That's one of the examples why I am looking for a general way to
    distinguish the case where the using class may delete the returned
    object and the case where it shouldn't.


    Michael
    Michael Pradel, Jan 26, 2007
    #7
  8. > What about returning a const ptr. Shouldn't that prevent the deletion?

    You mean the following?

    const Dummy* getDummy() {
    return d;
    }


    That doesn't work. The deletion works nevertheless.


    Michael
    Michael Pradel, Jan 26, 2007
    #8
  9. JLS wrote:
    >
    > On Jan 26, 1:42 pm, "Daniel T." <> wrote:
    >> In article <epdc6p$>, Noah Roberts <>
    >> wrote:
    >>
    >>> Michael Pradel wrote:
    >>>> Hi all,
    >>>> I just returned to C++ after programming in other languages, and find
    >>>> myself a bit puzzled about the following issue: (see attached code).
    >>>> How can I avoid the deletion of an object that is returned by a method?
    >>>> Is returning a clone of it the only possibility? That would use a lot of
    >>>> memory in case of larger objects.
    >>> You could use boost::shared_ptr with a nop deleter.
    >>> void noop() {}
    >>> shared_ptr<T> getT() const { return shared_ptr<T>(t, bind(&noop)); }delete &*getT();

    >
    > What about returning a const ptr. Shouldn't that prevent the deletion?


    Nope.


    --
    Clark S. Cox III
    Clark S. Cox III, Jan 26, 2007
    #9
  10. Michael Pradel

    Pete Becker Guest

    Noah Roberts wrote:
    > Michael Pradel wrote:
    >> Hi all,
    >>
    >> I just returned to C++ after programming in other languages, and find
    >> myself a bit puzzled about the following issue: (see attached code).
    >>
    >> How can I avoid the deletion of an object that is returned by a method?
    >> Is returning a clone of it the only possibility? That would use a lot of
    >> memory in case of larger objects.

    >
    > You could use boost::shared_ptr with a nop deleter.
    >
    > void noop() {}
    >
    > shared_ptr<T> getT() const { return shared_ptr<T>(t, bind(&noop)); }
    >


    void noop(void*){}
    return shared_ptr<T>(t, noop);

    > However, I imagine this could cause problems if you where to finish
    > using all the shared_ptrs that have deleters before this returned one
    > went out of scope. Unfortunately shared_ptr is the only one that
    > appears to let you override the deleter.


    Not if the code is using shared_ptr correctly. It should create a
    shared_ptr object that manages the newly created T object. Everything
    that needs the T object gets a copy of that shared_ptr. But in that
    case, the noop deleter is wrong: the last shared_ptr object should
    delete the object.

    The real problem is that you can't dictate application-level policies
    with low-level classes. If somebody wants to go out of their way to
    delete something that they're not supposed to delete, there's not much
    you can do about it. So don't bother.

    --

    -- Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com)
    Author of "The Standard C++ Library Extensions: a Tutorial and
    Reference." (www.petebecker.com/tr1book)
    Pete Becker, Jan 26, 2007
    #10
  11. Michael Pradel

    Pete Becker Guest

    Michael Pradel wrote:
    >
    > That's one of the examples why I am looking for a general way to
    > distinguish the case where the using class may delete the returned
    > object and the case where it shouldn't.
    >


    Document what the rules are. It's not your job to force other progammers
    to be competent.

    --

    -- Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com)
    Author of "The Standard C++ Library Extensions: a Tutorial and
    Reference." (www.petebecker.com/tr1book)
    Pete Becker, Jan 26, 2007
    #11
  12. Michael Pradel

    Daniel T. Guest

    Michael Pradel <> wrote:

    > > By simply observing a simple rule: the code responsible for allocating
    > > an object is also solely responsible for that object's deallocation.

    >
    > That's a good solution for most of the cases, but e.g. using the Factory
    > Method design pattern it doesn't make sense.


    The rule also doesn't make sense for a system that makes extensive use
    of smart pointers (most of which do delete objects but don't create
    them.)

    Just document who is in charge of deleting what and you will be fine.
    It's the C++ way.
    Daniel T., Jan 26, 2007
    #12
  13. Michael Pradel wrote:
    >> By simply observing a simple rule: the code responsible for allocating
    >> an object is also solely responsible for that object's deallocation.

    >
    > That's a good solution for most of the cases, but e.g. using the Factory
    > Method design pattern it doesn't make sense. Here one class is only
    > responsible for creating the object while another will use it and knowns
    > when to delete it.
    >
    > Product* Factory::create(ProductID id) {
    > if (id == A) return new ProductA;
    > // ...
    > }
    >
    > That's one of the examples why I am looking for a general way to
    > distinguish the case where the using class may delete the returned
    > object and the case where it shouldn't.


    Outside of establishing a set of rules, documenting them, and expecting
    others to follow them, there is no general way to distinguish the two cases.

    --
    Clark S. Cox III
    Clark S. Cox III, Jan 26, 2007
    #13
  14. On Fri, 26 Jan 2007 20:26:44 +0100, Michael Pradel wrote:
    >> By simply observing a simple rule: the code responsible for allocating
    >> an object is also solely responsible for that object's deallocation.

    >
    >That's a good solution for most of the cases, but e.g. using the Factory
    > Method design pattern it doesn't make sense.


    only when you use F.M. like Gamma et al.

    >Here one class is only
    >responsible for creating the object while another will use it and knowns
    >when to delete it.


    It makes perfect sense when the factory remains the owner of the
    created objets. This is called the "Creator as Sole Owner" pattern.
    See: http://www.ddj.com/184409895 and
    http://www.artima.com/intv/modern3.html

    Best wishes,
    Roland Pibinger
    Roland Pibinger, Jan 26, 2007
    #14
  15. Michael Pradel

    kwikius Guest

    On 26 Jan, 16:51, Michael Pradel <> wrote:
    > Hi all,
    >
    > I just returned to C++ after programming in other languages, and find
    > myself a bit puzzled about the following issue: (see attached code).
    >
    > How can I avoid the deletion of an object that is returned by a method?
    > Is returning a clone of it the only possibility? That would use a lot of
    > memory in case of larger objects.
    >
    > Thanks a lot!
    >
    > Michael
    >
    > #include <iostream>
    > using namespace std;
    >
    > class Dummy {
    > public:
    > int n;
    >
    > };class Victim {
    > private:
    > Dummy *d;
    >
    > public:
    > Victim() {
    > d = new Dummy();
    > d->n = 23;
    > }
    >
    > Dummy* getDummy() {
    > return d;
    > }
    >
    > };int main(int argc, char **argv) {
    > Victim *v = new Victim();
    > Dummy *d = v->getDummy();
    >
    > cout << d->n << endl;
    > delete d;
    > cout << d->n << endl;


    Can also make destructor of object private. In code below pointer is
    nullified by delete_and clean. raw delete won't compile. Of course
    creating stack objects will fail too..


    #include <iostream>

    template <typename T>
    inline
    void delete_and_clean ( T* & rp)
    {
    delete rp;
    rp =0;
    }

    struct my{
    int n;
    my():n(42){}

    private:
    friend void delete_and_clean<my> ( my* & );
    ~my(){}
    };

    int main()
    {
    my * p = new my;

    if ( p ){
    std::cout << p->n << '\n';
    }

    //compile fail due to private dtor
    // delete p;

    delete_and_clean(p);

    if ( p ){
    std::cout << "Bad news : " << p->n << '\n';
    }
    else {
    std::cout << "p died\n";
    }

    }
    kwikius, Jan 26, 2007
    #15
  16. Michael Pradel

    Daniel T. Guest

    "kwikius" <> wrote:

    > Can also make destructor of object private. In code below pointer is
    > nullified by delete_and clean. raw delete won't compile. Of course
    > creating stack objects will fail too..
    >
    >
    > #include <iostream>
    >
    > template <typename T>
    > inline
    > void delete_and_clean ( T* & rp)
    > {
    > delete rp;
    > rp =0;
    > }
    >
    > struct my{
    > int n;
    > my():n(42){}
    >
    > private:
    > friend void delete_and_clean<my> ( my* & );
    > ~my(){}
    > };
    >
    > int main()
    > {
    > my * p = new my;
    >
    > if ( p ){
    > std::cout << p->n << '\n';
    > }
    >
    > //compile fail due to private dtor
    > // delete p;
    >
    > delete_and_clean(p);
    >
    > if ( p ){
    > std::cout << "Bad news : " << p->n << '\n';
    > }
    > else {
    > std::cout << "p died\n";
    > }
    >
    > }


    But anyone who has access to delete_and_clean can delete the object, so
    that doesn't restrict access...

    You could make op& private and not implement it. Then the only parts of
    the code that can delete the object will be those that have a pointer to
    it. Then restrict who has a pointer to the object (other code only gets
    references to the object.) Of course, then you won't be able to put the
    object in any standard containers.

    Another idea is to wrap the object in a smart pointer that doesn't
    implement op*() [it only implements op->()]. But then the caller will be
    unable to pass the object to something that requires a reference.

    Frankly, I think any solution would cause more problems than it solves.
    Just document ownership, that's all you can do.
    Daniel T., Jan 27, 2007
    #16
  17. Michael Pradel

    AndrewD Guest

    Michael,

    Make Dummy destructor private and class Victim a friend as follows:

    class Dummy {
    public:
    int n;
    private:
    ~Dummy() {}
    friend class Victim;
    };

    The compiler will reject the delete code in main() with something
    like:
    F:\dummy\dummy.cpp(38) : error C2248: 'Dummy::~Dummy' : cannot access
    private member declared in class 'Dummy'
    but will allow delete of Dummy's by Victim.

    ....AndrewD...
    AndrewD, Jan 27, 2007
    #17
  18. Michael Pradel

    Daniel T. Guest

    "AndrewD" <> wrote:

    > Michael,
    >
    > Make Dummy destructor private and class Victim a friend as follows:
    >
    > class Dummy {
    > public:
    > int n;
    > private:
    > ~Dummy() {}
    > friend class Victim;
    > };


    Now Dummy can't be created by any other class than Victim. That's a
    rather draconian restriction just to safe-guard some data from delete.
    Daniel T., Jan 27, 2007
    #18
  19. On Sat, 27 Jan 2007 17:00:50 GMT, "Daniel T." <> wrote:

    >"AndrewD" <> wrote:
    >
    >> Michael,
    >>
    >> Make Dummy destructor private and class Victim a friend as follows:
    >>
    >> class Dummy {
    >> public:
    >> int n;
    >> private:
    >> ~Dummy() {}
    >> friend class Victim;
    >> };

    >
    >Now Dummy can't be created by any other class than Victim. That's a
    >rather draconian restriction just to safe-guard some data from delete.


    How's that? The default constructor is still public. Anyone can construct
    dummy, but only Victim (or Dummy) can destroy it.

    -dr
    Dave Rahardja, Jan 27, 2007
    #19
  20. Michael Pradel

    Daniel T. Guest

    Dave Rahardja <> wrote:
    > On Sat, 27 Jan 2007 17:00:50 GMT, "Daniel T." <> wrote:
    > >"AndrewD" <> wrote:
    > >
    > >> Michael,
    > >>
    > >> Make Dummy destructor private and class Victim a friend as follows:
    > >>
    > >> class Dummy {
    > >> public:
    > >> int n;
    > >> private:
    > >> ~Dummy() {}
    > >> friend class Victim;
    > >> };

    > >
    > >Now Dummy can't be created by any other class than Victim. That's a
    > >rather draconian restriction just to safe-guard some data from delete.

    >
    > How's that? The default constructor is still public. Anyone can construct
    > dummy, but only Victim (or Dummy) can destroy it.


    Sorry, I was thinking about auto variables, however as a practical
    matter not being able to destroy what you create can be a big problem.
    Daniel T., Jan 27, 2007
    #20
    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. bill
    Replies:
    3
    Views:
    982
    Damien
    Oct 10, 2005
  2. Graham Fawcett

    Re: Exposing object model in Python?

    Graham Fawcett, Jun 27, 2003, in forum: Python
    Replies:
    0
    Views:
    701
    Graham Fawcett
    Jun 27, 2003
  3. MK
    Replies:
    8
    Views:
    754
    Jason Whitlark
    Jul 1, 2003
  4. daokfella
    Replies:
    1
    Views:
    318
    bruce barker
    Aug 8, 2008
  5. X X
    Replies:
    4
    Views:
    336
    red floyd
    Jul 19, 2010
Loading...

Share This Page