 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
MirzaD Guest
|
Posted: Wed Nov 30, 2005 1:44 am Post subject: Unexpected destructor call |
|
|
Hello
Below you will find the problematic program. It is a string wrapper
class with a bare minimum of functionality to keep things simple.
**Code**
//StringClass.h
#include <iostream>
#include <cstring>
using namespace std;
class StringClass{
char* s;
int size;
public:
char* name;
StringClass();
StringClass(char* string);
~StringClass();
StringClass operator=(StringClass &string);
friend ostream &operator<<(ostream &output, StringClass &string);
};
StringClass::StringClass(){
s = new char[0];
size = 0;
}
StringClass::StringClass(char* string){
size = strlen(string);
s = new char[size + 1];
strcpy(s,string);
}
StringClass::~StringClass(){
cout << "Destructor: " << name << endl;
delete [] s;
}
StringClass StringClass::operator=(StringClass &string){
char* temp;
try{
temp = new char[string.size + 1];
}
catch (bad_alloc ex){
exit(1);
}
strcpy(temp,string.s);
delete [] s;
s = temp;
return *this;
}
ostream &operator<<(ostream &out, StringClass &string){
out << string.s;
return out;
}
//StringClass.cpp
#include "StringClass.h"
using namespace std;
int main(){
StringClass s("test"),t;
s.name = "s";
t.name = "t";
t = s;
cout << "s = " << s << endl << "t = " << t <
return 0;
}
**End code**
The above will display the following:
Destructor: t
s = test;
t = $*%
Destructor: t
Destructor: s
Why is the destructor called on t after the assignment takes place?
This would make sense to me if t was a pointer and the destructor was
called on the object it was pointing to (the last reference to the
object was just removed). Any help/clarification is appreciated.
Regards,
Mirza
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Antoun Kanawati Guest
|
Posted: Wed Nov 30, 2005 10:49 am Post subject: Re: Unexpected destructor call |
|
|
MirzaD wrote:
| Quote: | Hello
Below you will find the problematic program. It is a string wrapper
class with a bare minimum of functionality to keep things simple.
**Code**
//StringClass.h
#include
#include
using namespace std;
class StringClass{
char* s;
int size;
public:
char* name;
StringClass();
StringClass(char* string);
~StringClass();
StringClass operator=(StringClass &string);
friend ostream &operator<<(ostream &output, StringClass &string);
};
...
int main(){
StringClass s("test"),t;
s.name = "s";
t.name = "t";
t = s;
cout << "s = " << s << endl << "t = " << t <
return 0;
}
**End code**
The above will display the following:
Destructor: t
s = test;
t = $*%
Destructor: t
Destructor: s
Why is the destructor called on t after the assignment takes place?
This would make sense to me if t was a pointer and the destructor was
called on the object it was pointing to (the last reference to the
object was just removed). Any help/clarification is appreciated.
Regards,
Mirza
|
Your operator= returns an instance by value, creating a temporary.
You don't define a copy constructor, so you get a memberwise copy.
So, the temporary will be destroyed when it ceases to be necessary,
hence the unexpected destructor call. Then, because of the memberwise
copy of the pointer, the object named 't' points holds a dangling
pointer, which explains the funny output for 't=...'.
--
A. Kanawati
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Zara Guest
|
Posted: Wed Nov 30, 2005 10:53 am Post subject: Re: Unexpected destructor call |
|
|
On 29 Nov 2005 20:44:09 -0500, "MirzaD" <mirza_bb (AT) yahoo (DOT) com> wrote:
| Quote: | Hello
Below you will find the problematic program. It is a string wrapper
class with a bare minimum of functionality to keep things simple.
**Code**
//StringClass.h
(...)
Why is the destructor called on t after the assignment takes place?
This would make sense to me if t was a pointer and the destructor was
called on the object it was pointing to (the last reference to the
object was just removed). Any help/clarification is appreciated.
Regards,
Mirza
|
Try to log also constructors, not only destructors. You will probably
find there are some temporaries created.
Regards,
-- Zara
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Maeder Guest
|
Posted: Wed Nov 30, 2005 10:53 am Post subject: Re: Unexpected destructor call |
|
|
"MirzaD" <mirza_bb (AT) yahoo (DOT) com> writes:
| Quote: | //StringClass.h
#include <iostream
|
Note. For your program to portably have defined behavior, also
#include
for char const * are in that header, and
#include it.
| Quote: | #include
using namespace std;
|
I'd never put a using directive into a header, particularly not at
global namespace level. What if some code wants to use class
StringClass but doesn't want the using directive?
| Quote: | class StringClass{
char* s;
int size;
public:
char* name;
StringClass();
StringClass(char* string);
~StringClass();
StringClass operator=(StringClass &string);
friend ostream &operator<<(ostream &output, StringClass &string);
};
|
[snip]
| Quote: | int main(){
StringClass s("test"),t;
s.name = "s";
t.name = "t";
t = s;
cout << "s = " << s << endl << "t = " << t <
return 0;
}
**End code**
The above will display the following:
Destructor: t
s = test;
t = $*%
Destructor: t
Destructor: s
Why is the destructor called on t after the assignment takes place?
|
Because your program does something in an unusual way (since this
looks an exercise, I don't want to give away everything and take away
the fun).
There is one StringClass special member function that the compiler
currently generates for you (in a way that causes your program to have
undefined behavior, by the way). Implement it yourself and instrument
it with trace output along the lines of what the other functions do,
and I'm confident you'll understand what's going on.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
DAkhlynin@yandex.ru Guest
|
Posted: Wed Nov 30, 2005 10:59 am Post subject: Re: Unexpected destructor call |
|
|
Problem is in operator=() signature
| Quote: | StringClass StringClass::operator=(StringClass &string)
|
Here you return StringClass by value, without defining copy
constructor.
So to make your piece of code run you should change signature to:
StringClass& StringClass::operator=(const StringClass &string).
However you also should implement copy constructor or make it private
and not implemented. In other case undefined behavior can appear.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ulrich Eckhardt Guest
|
Posted: Wed Nov 30, 2005 11:02 am Post subject: Re: Unexpected destructor call |
|
|
MirzaD wrote:
| Quote: | Below you will find the problematic program. It is a string wrapper
class with a bare minimum of functionality to keep things simple.
|
No, less than the bare minimum in fact. You forgot that the compiler
generates a few functions itself if you don't supply them. Please google
for the so-called "Law of Three".
Uli
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ralf Fassel Guest
|
Posted: Wed Nov 30, 2005 11:49 am Post subject: Re: Unexpected destructor call |
|
|
* "MirzaD" <mirza_bb (AT) yahoo (DOT) com>
| Quote: | Why is the destructor called on t after the assignment takes place?
|
Because your assignment operator returns a temporary object, not a
reference.
The usual signature for the assignment operator is
StringClass & operator=(const StringClass &string);
where you have
StringClass operator=(StringClass &string);
If you just print the 'this' pointer in the CTOR and DTOR (and not the
'name' kludge), you'll see that it is neither t nor s which is
destroyed:
CTOR2 0xbfffd7b0
CTOR1 0xbfffd7a0
Destructor: 0xbfffd790 t
Note that the temporary CTOR does not show up here since the compiler
generated default copy-CTOR is used.
This copy-CTOR leads to a double-free problem when the char array is
free'd in the DTOR:
CTOR2 0xbfffe730
CTOR1 0xbfffe720
Destructor: t, 0xbfffe710, freeing 0x804b028
s = test
t = ....
Destructor: t, 0xbfffe720, freeing 0x804b028
*** glibc detected *** double free or corruption (fasttop): 0x0804b028 ***
Abort
BTW,
try{
temp = new char[string.size + 1];
}
catch (bad_alloc ex){
exit(1);
}
is not very user friendly if this is supposed to be a library class.
Why catch this? If the user does not catch it, the program will abort
anyway.
R'
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
dayton Guest
|
Posted: Wed Nov 30, 2005 11:50 am Post subject: Re: Unexpected destructor call |
|
|
MirzaD wrote:
| Quote: | StringClass StringClass::operator=(StringClass &string){
|
Your assignment operator is returning by value rather than by
reference. Try
StringClass& StringClass::operator=(StringClass &string){
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze Guest
|
Posted: Wed Nov 30, 2005 2:42 pm Post subject: Re: Unexpected destructor call |
|
|
MirzaD wrote:
| Quote: | Below you will find the problematic program. It is a string
wrapper class with a bare minimum of functionality to keep
things simple.
|
There is more than one problem.
| Quote: | **Code**
//StringClass.h
#include <iostream
|
Technically, you also need "#include
to be conform.
And you don't need this:-).
| Quote: | using namespace std;
class StringClass{
char* s;
|
I'm wondering, given the rest of the code, if this shouldn't be
char const* s ;
| Quote: | int size;
public:
char* name;
|
Same thing here.
But I suspect that is only something you've thrown in for
debugging purposes. Are you aware that you can output the this
pointer. It will display an address, typically some mysterious
hex number, but if the numbers are the same, its the same
object, and if they aren't it isn't.
| Quote: | StringClass();
StringClass(char* string);
|
Definitely needs a const after char here.
| Quote: | ~StringClass();
StringClass operator=(StringClass &string);
|
And a const after StringClass here. In addition, the return
value shoud be a reference. (This is where the extra object is
coming from later.)
| Quote: | friend ostream &operator<<(ostream &output, StringClass &string);
};
|
You're missing a copy constructor. That, and given the extra
object you're generating because of the missing reference in the
assignment operator, is sending you right into undefined
behavior land. Since you don't provide a copy constructor, the
compiler generates one according to its rules. And its rules
are the simplest possible: object by object copy. Which means
that you have two instances sharing the same imagine of s (and
the same "name", which explains the misleading output).
The simplest solution here is to do a deep copy:
StringClass::StringClass( StringClass const& other )
: s( new char[ other.size + 1 ] )
, size( other.size )
, name( "unknown" )
{
strcpy( s, other.s ) ;
}
| Quote: | StringClass::StringClass(){
s = new char[0];
size = 0;
}
|
Given the rest of the implementation, this will get you into
trouble if you ever assign from a default constructed object.
If you don't want to need special handling for the empty string
case:
s = new char[ 1 ] ;
*s = ' ' ;
size = 0 ;
If you accept the special handling, just set s = NULL, and
forget about the new.
Of course, I generally prefer using initializer lists, as in the
copy constructor, so:
StringClass::StringClass()
: s( new char[ 1 ] )
: size( 0 )
{
*s = ' ' ;
}
| Quote: | StringClass::StringClass(char* string){
size = strlen(string);
s = new char[size + 1];
strcpy(s,string);
}
|
Same remark concerning initializer lists. Although it is a bit
more delicate here -- regardless of the order in the initializer
list, s will be initialized before size. Changing the order of
the two declarations in the class definition fixes this.
| Quote: | StringClass::~StringClass(){
cout << "Destructor: " << name << endl;
delete [] s;
}
|
Since you're tracing object lifetime: put a print statement in
all of the constructors as well, and display the this. Also,
ensure that name always contains something that can be
displayed -- setting it to "unknown" in all of the constructors
should do the trick.
| Quote: | StringClass StringClass::operator=(StringClass &string){
char* temp;
try{
temp = new char[string.size + 1];
}
catch (bad_alloc ex){
exit(1);
}
|
You let bad_alloc propagate everywhere else (which I think is
probably right), why not here?
| Quote: | strcpy(temp,string.s);
delete [] s;
s = temp;
return *this;
}
|
And a somewhat personal question: no copy constructor, and no
const, pretty much point to a beginner, at least with regards to
C++. And String itself is a typically beginner's training
exercise (in production code, one uses std::string). So... where
on earth did you learn to structure your assignment operator for
exception safety? I know far to many experienced programmers
who get this wrong.
| Quote: | ostream &operator<<(ostream &out, StringClass &string){
out << string.s;
return out;
}
//StringClass.cpp
#include "StringClass.h"
using namespace std;
int main(){
StringClass s("test"),t;
|
Constructs two StringClass objects, named s and t. If we ever
use t on the right side of an assignment, however, we're in
trouble.
| Quote: | s.name = "s";
t.name = "t";
t = s;
|
Does the assignment, then... returns a copy of the assigned
object. That copy is a temporary, and so gets destructed at the
end of the full expression.
Note that this means that the destructor is called, which in
turn means that the dynamic memory is freed. Which is a bit of
a shame, because the variable t still has a pointer to it, will
continue to use it, and will also free it in its destructor.
Normally, assignment operators return a reference, not a copy of
the object. But this wouldn't have been a problem here if you'd
have had a correct copy constructor. And of course, there will
be functions where you really do want to return a copy of the
object, and the copy constructor had better work for them.
| Quote: | cout << "s = " << s << endl << "t = " << t <
return 0;
}
**End code**
The above will display the following:
Destructor: t
s = test;
t = $*%
Destructor: t
Destructor: s
Why is the destructor called on t after the assignment takes
place?
|
Because your assignment operator returns a copy of the object,
and you have no way of seeing that you aren't dealing with the
same object.
The funny value for t is due to the fact that you don't have a
copy constructor, and t.s is pointing to memory that was deleted
in the destructor of the temporary. Apparently, in your
implementation, some code in the iostream has reallocated this
memory, and written something else to it.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Udo Stenzel Guest
|
Posted: Thu Dec 01, 2005 3:08 am Post subject: Re: Unexpected destructor call |
|
|
MirzaD <mirza_bb (AT) yahoo (DOT) com> schrieb:
Well, there are already answers that point to your immediate problem,
but I'm surprised nobody pointed this out, yet:
| Quote: | catch (bad_alloc ex){
exit(1);
}
|
This snippet is wrong on so many accounts, I really don't know where to
begin. Whoever told you that you have to catch every exception,
deserves a spanking. Ask youself what happens if you remove these three
lines, and then remove them.
Udo.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|