Preventing delete

  • Thread starter =?ISO-8859-1?Q?Xavier_D=E9coret?=
  • Start date
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label);
}
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};

int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}
 
P

Pete C

Xavier said:
The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

Make the destructor private or protected, and make the user call a
custom function that you supply to do the deleting.
 
G

Gavin Deane

Xavier said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label);
}
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};

int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}

Are you asking purely out of academic interest? Because the real world
answer to that particular code is, for the class designer: "don't
expose the internals of your class like that" and for the client code:
"don't delete memory you don't own".

In other words, can you provide a more realistic example of the problem
you are trying to solve.

Gavin Deane
 
B

Ben Pope

Xavier said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

Create your own type and override operator delete?

Ben Pope
 
G

Gavin Deane

Pete said:
Make the destructor private or protected, and make the user call a
custom function that you supply to do the deleting.

That works for your own types. The OP's code was deleting a pointer to
char. I don't know of anything you can do to a pointer to a built in
type that prevents it being deleted (apart from wrapping it in a
class).

Gavin Deane
 
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Are you asking purely out of academic interest? Because the real world
answer to that particular code is, for the class designer: "don't
expose the internals of your class like that" and for the client code:
"don't delete memory you don't own".


I don't buy this argument: encapsulation is meant to prevent tthe API
user to shoot himself in the foot. If you expect the programmer to be
intelligent, you do not need the const qualifier. Just trust the
programmer not to modify an object he is not allowed to ;-). And you do
not need smart pointers. Just trust the programmer to delete cleanly
every chunk of memory. And so on...

Regarding exposure of internal data, this happens all the time! Or your
class is generally useless. If you want a less academic example, just try:

#include <string>
#include <iostream>

using namespace std;

int main()
{
string s("foobar");

cout<<s<<endl;

delete [] s.c_str();

cout<<s<<endl;

return 0;
}

You cannot not do simpler. It is using a standard class (which is
developped by valuable people according to Scott Meyers ;-)). The class
must return pointer to raw data. Yet I can delete it and the above
program dumps in an unpleasant manner.

foobar
*** glibc detected *** ./a.out: free(): invalid pointer: 0x087041e4 ***
======= Backtrace: =========
/lib/libc.so.6[0x3fe124]
/lib/libc.so.6(__libc_free+0x77)[0x3fe65f]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x66b0a9]
/usr/lib/libstdc++.so.6(_ZdaPv+0x1d)[0x66b0f5]
../a.out(__gxx_personality_v0+0x1a9)[0x8048909]
/lib/libc.so.6(__libc_start_main+0xdf)[0x3afd5f]
../a.out(__gxx_personality_v0+0x61)[0x80487c1]
======= Memory map: ========
0022d000-00247000 r-xp 00000000 08:01 655395 /lib/ld-2.3.5.so
00247000-00248000 r--p 00019000 08:01 655395 /lib/ld-2.3.5.so
00248000-00249000 rw-p 0001a000 08:01 655395 /lib/ld-2.3.5.so
0024d000-0024e000 r-xp 0024d000 00:00 0 [vdso]
00376000-00399000 r-xp 00000000 08:01 659189 /lib/libm-2.3.5.so
00399000-0039a000 r--p 00022000 08:01 659189 /lib/libm-2.3.5.so
0039a000-0039b000 rw-p 00023000 08:01 659189 /lib/libm-2.3.5.so
0039b000-004be000 r-xp 00000000 08:01 655494 /lib/libc-2.3.5.so
004be000-004c0000 r--p 00123000 08:01 655494 /lib/libc-2.3.5.so
004c0000-004c2000 rw-p 00125000 08:01 655494 /lib/libc-2.3.5.so
004c2000-004c4000 rw-p 004c2000 00:00 0
005a0000-005a9000 r-xp 00000000 08:01 659349
/lib/libgcc_s-4.0.2-20051126.so.1
005a9000-005aa000 rw-p 00009000 08:01 659349
/lib/libgcc_s-4.0.2-20051126.so.1
005c1000-00696000 r-xp 00000000 08:01 1717334 /usr/lib/libstdc++.so.6.0.7
00696000-0069b000 rw-p 000d5000 08:01 1717334 /usr/lib/libstdc++.so.6.0.7
0069b000-006a0000 rw-p 0069b000 00:00 0
08048000-08049000 r-xp 00000000 00:12 1945771
/home/artis/decoret/tmp/a.out
08049000-0804a000 rw-p 00000000 00:12 1945771
/home/artis/decoret/tmp/a.out
08704000-08725000 rw-p 08704000 00:00 0 [heap]
b7e00000-b7e21000 rw-p b7e00000 00:00 0
b7e21000-b7f00000 ---p b7e21000 00:00 0
b7f2a000-b7f2c000 rw-p b7f2a000 00:00 0
b7f45000-b7f47000 rw-p b7f45000 00:00 0
bf931000-bf947000 rw-p bf931000 00:00 0 [stack]
Abort
 
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Gavin said:
That works for your own types. The OP's code was deleting a pointer to
char. I don't know of anything you can do to a pointer to a built in
type that prevents it being deleted (apart from wrapping it in a
class).

Gavin Deane

Yep. The Invoice dummy example is also deleting a const pointer on a
custom type, so it is not a problem of builtin types.
 
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Xavier said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?


BTW, this seems a recurring question:

http://www.codecomments.com/C_Modurated_group/message719344-1.html

The important point is "how to control what the users do with pointer".
The const qualifier prevents him to modify it but not to delete it.
 
M

Michiel.Salters

Xavier said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

Sure you can. You can't assign a new value, or null, but delete doesn't
actually change the pointer value. (Or do you mean a pointer-to-const,
which is another beast?)
class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;
}

Const pointer to const Dummy, but obviously you can delete it.
p doesn't change, nor does *p. The end of a lifetime doesn't count
as a change, just like the beginning doesn't. There are no const
ctors or dtors, either.

The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

You can't. The user can always do stupid things like 'delete new
Dummy[2];'
Point is, the users we're talking about are programmers. They're smart
enough to read the manual, at least in theory.
#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label);
}
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};

Whoa. Programmers are supposed to read the manual, remember?
That includes you. strdup isn't standard C++. All implementations
I know require you to use free(). I bet yours does as well. However,
I don't suggest you change that. Read on instead.
int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}

That should trigger a warning or two. Invoice::Invoice takes a
non-const char*
but "train" is definitely a const literal.

The solution is of course very simple: don't mix C and C++ like you do.
Drop
<string.h> and use <string>. Invoice::Invoice should take a std::string
const&.
label() should return a string const&. The data member should be a
std::string.
Drop all strdup()s, free()s, delete[]s and just assign strings like
they're ints.

As a bonus, performance may even increase.

HTH,
Michiel Salters
 
G

Gavin Deane

Xavier said:
I don't buy this argument: encapsulation is meant to prevent tthe API
user to shoot himself in the foot. If you expect the programmer to be
intelligent, you do not need the const qualifier. Just trust the
programmer not to modify an object he is not allowed to ;-).

There's a difference between shooting yourself in the foot by accident
because your gun (API) doesn't have a safety catch (const) and shooting
yourself in the face because you have no idea how a gun works or what
you can do with it (are prone to passing things to delete just because
they are pointers to dynamically allocated memory).
And you do
not need smart pointers. Just trust the programmer to delete cleanly
every chunk of memory. And so on...

But the code to manage that memory in an exception safe manner is more
complicated without smart pointers. A competent C++ programmer could
still write it but it would take longer, be harder to read and be
harder to modify correctly. That's what smart pointers are for.
Regarding exposure of internal data, this happens all the time! Or your
class is generally useless. If you want a less academic example, just try:

OK, fair enough.
#include <string>
#include <iostream>

using namespace std;

int main()
{
string s("foobar");

cout<<s<<endl;

delete [] s.c_str();

That's still an academic example because no C++ programmer who knows
anything about C++ programming would ever do that. If you've got
developers around who do that sort of thing, no amount tightening the
restrictions imposed by your API is going to turn them into competent
programmers. They'll still produce garbage.
cout<<s<<endl;

return 0;
}

You cannot not do simpler. It is using a standard class (which is
developped by valuable people according to Scott Meyers ;-)). The class
must return pointer to raw data. Yet I can delete it and the above
program dumps in an unpleasant manner.

std::string doesn't prevent you from writing code like this. So who is
at fault? Scott Meyers's valuable people for writing a fragile API, or
you for doing something stupid?

Gavin Deane
 
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Xavier said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:


Sure you can. You can't assign a new value, or null, but delete doesn't
actually change the pointer value. (Or do you mean a pointer-to-const,
which is another beast?)

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;
}


Const pointer to const Dummy, but obviously you can delete it.
p doesn't change, nor does *p. The end of a lifetime doesn't count
as a change, just like the beginning doesn't. There are no const
ctors or dtors, either.


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?


You can't. The user can always do stupid things like 'delete new
Dummy[2];'
Point is, the users we're talking about are programmers. They're smart
enough to read the manual, at least in theory.

#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label);
}
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};


Whoa. Programmers are supposed to read the manual, remember?
That includes you. strdup isn't standard C++. All implementations
I know require you to use free(). I bet yours does as well. However,
I don't suggest you change that. Read on instead.

int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}


That should trigger a warning or two. Invoice::Invoice takes a
non-const char*
but "train" is definitely a const literal.

The solution is of course very simple: don't mix C and C++ like you do.
Drop
<string.h> and use <string>. Invoice::Invoice should take a std::string
const&.
label() should return a string const&. The data member should be a
std::string.
Drop all strdup()s, free()s, delete[]s and just assign strings like
they're ints.

As a bonus, performance may even increase.

Sure. That's what do most of the time (even if some ccompilers still
forces me to use string.h and math.h instead of cstring and cmath).

I just stripped the example to bare minimum.

The post was just because I am disappointed that C++ allows you to write
delete [] "tmp". I falsely thought there would be a way to prevent this.

Illusions are gone and I found many (sometimes religious) threads about
this topic while I was waiting for answers on that forum.

Too bad.

PS: of course we are talking about programmers. Most of those that I
have seen, even in big companies, are bad programmers. I like to think
I am not too bad, but I am sure there is still a long way to go. So the
more I can encapsulate, the better I feel.
 
P

Pete C

Xavier said:
I don't buy this argument: encapsulation is meant to prevent tthe API
user to shoot himself in the foot.

If you don't buy this argument, you shouldn't be programming C++. Try
Java instead. Not deleting memory you don't own is fairly basic stuff.
There is nothing in the language preventing you from doing:
free(getenv("TMP"));
but you don't do it because the memory returned is not yours to delete.
If you want to give out a const pointer to some internal structure, do
it - and document that the pointed-to object remains property of your
class. If you don't trust your users not to call delete on it, then
*they* shouldn't be programming C++.
 
P

Pete C

Xavier said:
The important point is "how to control what the users do with pointer".
The const qualifier prevents him to modify it but not to delete it.

It makes complete sense, really. If you declare a const local variable,
you have to be able to delete it when the function returns. Having a
separate rule for the heap would just be inconsistent for no good
reason.
 
?

=?ISO-8859-1?Q?Xavier_D=E9coret?=

Gavin said:
Xavier said:
Regarding exposure of internal data, this happens all the time! Or your
class is generally useless. If you want a less academic example, just try:


OK, fair enough.

#include <string>
#include <iostream>

using namespace std;

int main()
{
string s("foobar");

cout<<s<<endl;

delete [] s.c_str();


That's still an academic example because no C++ programmer who knows
anything about C++ programming would ever do that. If you've got
developers around who do that sort of thing, no amount tightening the
restrictions imposed by your API is going to turn them into competent
programmers. They'll still produce garbage.

Well, the point is that you can get you const char* through a long list
of function calls and you can easily be messed about the origin. Yes,
people, even in big companies write code like this:

MultiLingualString s("hello");

const char* translation = s.toFrench();

and then how do they know wether MultiLingualSring returned a const
char* to a temporary that you can use immediately but on which they
should not keep a pointer because they do not know how long it is going
to live. Or is it a pointer to a internally-kept-translation that is
assured to live as long s lives? The solution is RTFM, which is fine but
does place burden on the programmer. The proper solution on which I
agree is to never use pointer, but pass object (such as string) instead.
And use reference counted strings if performance is a matter. Good libs
like Qt does this.
std::string doesn't prevent you from writing code like this. So who is
at fault? Scott Meyers's valuable people for writing a fragile API, or
you for doing something stupid?

That's my fault for not using the gun correctly. Sure. Except that I
personnally do not have the problem. But I encounter it in many code I
review (and can you *guarantee* you have never done it?).

The discussion is obviously endless. My pseudo-conclusion is "C++
programmer must behave politely". That's fine enough in many cases. But
when it comes to automatic program checking, it is not satisfactory. As
far as I remember, the NASA shuttle exploded for a counter overflow.
Which beginner would do that mistake. Yet, someone at the NASA did it,
right?
If you read, Donald Norman's on Psychology of Everyday things, you'll
discover that in such cases, the "human weakness" is blamed but it is
not fair. The machine design is responsible for offering a dangerous,
not human-mind suited interface.

Anyway, thanks for you insights/point of view.
 
P

Pete Becker

Xavier said:
and then how do they know wether MultiLingualSring returned a const
char* to a temporary that you can use immediately but on which they
should not keep a pointer because they do not know how long it is going
to live. Or is it a pointer to a internally-kept-translation that is
assured to live as long s lives? The solution is RTFM, which is fine but
does place burden on the programmer.

Programming is all about understanding what you're doing. Programmers
who consider that a burden are in the wrong profession.
The proper solution on which I
agree is to never use pointer, but pass object (such as string) instead.

Maybe. More important is designing an interface so that it's clear when
things should be deleted and when they shouldn't.
 
P

Pete Becker

Xavier said:
The important point is "how to control what the users do with pointer".

The important point is how to make it less likely that users will do the
wrong thing with a pointer. The simplest solution is to document what
the right thing is. If that fails, look to more intrusive (and more
expensive) approaches.
 
J

Jim Langston

Xavier Décoret said:
Hi,

I long thought one could not delete a const pointer but the following code
compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label); }
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};

int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}

Show me dummy proof code and I'll show you a better dummy.
 
D

Daniel T.

Xavier Décoret said:
Hi,

I long thought one could not delete a const pointer but the following
code compiles under g++ and visual net:

class Dummy
{
public:
Dummy() {}
~Dummy() {}
};

int main(int,char**)
{
const Dummy* const p = new Dummy();
delete p;

return 0;
}


The question is: how can I forbid the user to delete some data I an
handling a pointer to? Here is an example of code that does compile but
will lead to segfault. How can I prevent it?

#include <iostream>
#include <string.h>

using namespace std;

class Invoice
{
public:
Invoice(char* label)
{
_label = strdup(label);
}
~Invoice()
{
delete [] _label;
}
const char* const label() const
{
return _label;
}
private:
char* _label;
};

int main(int,char**)
{
Invoice d("train");
delete [] d.label();

cout<<d.label()<<endl;

return 0;
}

Do not give the user access to the pointer and he can't delete it. In
the specific example above, it's a simple matter of changing the return
value of 'Invoice::label' to 'std::string Invoice::label() const'

BTW, 'strdup' is not standard, and likely does not new memory (rather it
probably 'malloc's the memory.) You probably shouldn't delete [] _label,
rather you should free it.
 
D

Daniel T.

Xavier Décoret said:
The discussion is obviously endless. My pseudo-conclusion is "C++
programmer must behave politely". That's fine enough in many cases. But
when it comes to automatic program checking, it is not satisfactory. As
far as I remember, the NASA shuttle exploded for a counter overflow.
Which beginner would do that mistake. Yet, someone at the NASA did it,
right?

Just for the record, I believe that was an Ariane 5 rocket, not a NASA
shuttle.
 
B

Ben Pope

Daniel said:
Just for the record, I believe that was an Ariane 5 rocket, not a NASA
shuttle.

And an overflow converting a number, the rocket thought it was upside
down and tried to correct its trajectory. Oops.

Ben Pope
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,123
Latest member
Layne6498
Top