473,406 Members | 2,954 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

Return object without exposing it to delete

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
Jan 26 '07 #1
22 1907
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<TgetT() 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.
Jan 26 '07 #2


On Jan 26, 8:51 am, Michael Pradel <mich...@binaervarianz.dewrote:
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

Jan 26 '07 #3
Michael Pradel <mi*****@binaervarianz.dewrote:
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.
Jan 26 '07 #4
In article <ep**********@aioe.org>, Noah Roberts <us**@example.net>
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<TgetT() const { return shared_ptr<T>(t, bind(&noop)); }
delete &*getT();
Jan 26 '07 #5
JLS


On Jan 26, 1:42 pm, "Daniel T." <danie...@earthlink.netwrote:
In article <epdc6p$is...@aioe.org>, Noah Roberts <u...@example.net>
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<TgetT() 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.

Jan 26 '07 #6
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
Jan 26 '07 #7
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
Jan 26 '07 #8
JLS wrote:
>
On Jan 26, 1:42 pm, "Daniel T." <danie...@earthlink.netwrote:
>In article <epdc6p$is...@aioe.org>, Noah Roberts <u...@example.net>
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<TgetT() 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
cl*******@gmail.com
Jan 26 '07 #9
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<TgetT() 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)
Jan 26 '07 #10
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)
Jan 26 '07 #11
Michael Pradel <mi*****@binaervarianz.dewrote:
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.
Jan 26 '07 #12
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
cl*******@gmail.com
Jan 26 '07 #13
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
Jan 26 '07 #14


On 26 Jan, 16:51, Michael Pradel <mich...@binaervarianz.dewrote:
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";
}

}

Jan 26 '07 #15
"kwikius" <an**@servocomm.freeserve.co.ukwrote:
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.
Jan 27 '07 #16
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...


Jan 27 '07 #17
"AndrewD" <an************@gmail.comwrote:
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.
Jan 27 '07 #18
On Sat, 27 Jan 2007 17:00:50 GMT, "Daniel T." <da******@earthlink.netwrote:
>"AndrewD" <an************@gmail.comwrote:
>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
Jan 27 '07 #19
Dave Rahardja <as*@me.comwrote:
On Sat, 27 Jan 2007 17:00:50 GMT, "Daniel T." <da******@earthlink.netwrote:
"AndrewD" <an************@gmail.comwrote:
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.
Jan 27 '07 #20
On Sat, 27 Jan 2007 21:03:25 GMT, "Daniel T." <da******@earthlink.netwrote:

>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.
As a general rule, yes. However, there are times where the Abstract Factory
pattern makes this pattern desirable:

class Widget
{
public:
/* ... */
virtual void fn() = 0;

private:
virtual ~Widget();
friend class AbstractFactory;
};

class WidgetA: public Widget
{
public:
/* ... */
virtual void fn();
};

class WidgetB: public Widget
{
public:
/* ... */
virtual void fn();
};

class AbstractFactory
{
public:
/* ... */

AbstractFactory& createFactory();

// Create either WidgetA or WidgetB
Widget& createWidget();
void destroyWidget(Widget&);
};

int main()
{
AbstractFactory& factory = AbstractFactory::createFactory();

Widget& widget = factory.create();
factory.destroy(widget);

/* ... */
}

Why not just overload the delete operator in Widget? Because then Widget needs
to know about AbstractFactory, and Widget potentially needs to carry a
reference to the Abstractfactory instance.

-dr
Jan 28 '07 #21
Dave Rahardja <as*@me.comwrote:
"Daniel T." <da******@earthlink.netwrote:

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.

As a general rule, yes. However, there are times where the Abstract Factory
pattern makes this pattern desirable.
But by making the destroy function public, you allow anyone who has a
pointer/reference to the object to delete it. The OP didn't want that to
be possible.
Jan 28 '07 #22

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<TgetT() 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.
It also seems that shared_ptr ought to be able to be manipulated so
that it isn't deleted in the first place.

Something like
shared_ptr<std::stringget_xml_color_string( int color )
{
shared_ptr<std::stringxml_string(new std::string);

xml_string += "<color>";
xml_string += *get_color_string( color );
xml_string += "</color>";
return xml_string;
}

....
for ( ... )
{
xml_menu_string += *get_xml_color_string( ... );
next_statement;
...
}
works under g++. Evidently the temporary for the return value is
created before xml_string goes out of scope, thus keeping the
shared_ptr reference count at 1 and keeping the object from being
destroyed until next_statement begins (as verified by trying this
pattern with some terminal output in the constructor and
destructor).

This saves a boatload of temporary objects in the loop.

But I can't think of a portable way to do the same thing.

Jan 31 '07 #23

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

94
by: John Bailo | last post by:
The c# *return* statement has been bothering me the past few months. I don't like the fact that you can have different code paths in a method and have multiple return statements. To me, it...
4
by: John Black | last post by:
Hi, I wonder what is the difference between ending the main() with and without exit(0)? My code is like this flow, class ClassA{ ClassB* cb; int toExit();
6
by: PengYu.UT | last post by:
Hi, Suppose I have the following class declaration with a pointer as the argument. And suppose I "new" an array "lengths" as the argument "edge_lengths". Now, the polygon object is valid....
1
by: Jack Addington | last post by:
I have a 3rd party object that fires an itemchanged event when someone edits some data on a form. This event has a custom eventArgs that has a field called ActionCode. In the code of the event,...
14
by: dcassar | last post by:
I have had a lively discussion with some coworkers and decided to get some general feedback on an issue that I could find very little guidance on. Why is it considered bad practice to define a...
7
by: pooba53 | last post by:
I am working with VB .NET 2003. Let's say my main form is called Form1. I have to launch a new form (Form2) that gathers input from the user. How can I pass variable information back to Form1...
5
by: Sergio Montero | last post by:
I have a MustInherits Base class that implements a custom IDataLayer interfase. IDataLayer expose CRUD methods. Base class constructor requires two parameters: ConnectionString TableName ...
19
by: klenwell | last post by:
Another request for comments here. I'd like to accomplish something like the scheme outlined at this page here: http://tinyurl.com/3dtcdr In a nutshell, the form uses javascript to hash...
0
by: =?Utf-8?B?SmVhbi1GcmFuY29pcyBCcmV0b24=?= | last post by:
"siddharthkhare@hotmail.com" wrote: The context is important in this kind of design concern : I assume there's a lot of user and that application will evolve to add richer functionality. My...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.