Return object without exposing it to delete

M

Michael Pradel

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
 
N

Noah Roberts

Michael said:
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.
 
G

Greg

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
 
D

Daniel T.

Michael Pradel said:
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.
 
D

Daniel T.

[QUOTE="Noah Roberts said:
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)); }[/QUOTE]

delete &*getT();
 
J

JLS

[QUOTE="Noah Roberts said:
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();
[/QUOTE]

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.
 
M

Michael Pradel

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
 
M

Michael Pradel

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
 
P

Pete Becker

Noah said:
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 said:
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)
 
P

Pete Becker

Michael said:
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)
 
D

Daniel T.

Michael Pradel said:
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.
 
C

Clark S. Cox III

Michael said:
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.
 
R

Roland Pibinger

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
 
K

kwikius

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";
}

}
 
D

Daniel T.

kwikius said:
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.
 
A

AndrewD

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...
 
D

Daniel T.

AndrewD said:
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.
 
D

Dave Rahardja

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
 
D

Daniel T.

Dave Rahardja said:
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.
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top