Unexpected destructor call

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • MirzaD

    #1

    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(cha r* string);

    ~StringClass();

    StringClass operator=(Strin gClass &string);
    friend ostream &operator<<(ost ream &output, StringClass &string);
    };

    StringClass::St ringClass(){
    s = new char[0];
    size = 0;
    }

    StringClass::St ringClass(char* string){
    size = strlen(string);
    s = new char[size + 1];
    strcpy(s,string );
    }

    StringClass::~S tringClass(){
    cout << "Destructor : " << name << endl;
    delete [] s;
    }

    StringClass StringClass::op erator=(StringC lass &string){
    char* temp;
    try{
    temp = new char[string.size + 1];
    }
    catch (bad_alloc ex){
    exit(1);
    }
    strcpy(temp,str ing.s);
    delete [] s;
    s = temp;
    return *this;
    }

    ostream &operator<<(ost ream &out, StringClass &string){
    out << string.s;
    return out;
    }


    //StringClass.cpp

    #include "StringClas s.h"

    using namespace std;

    int main(){
    StringClass s("test"),t;
    s.name = "s";
    t.name = "t";
    t = s;
    cout << "s = " << s << endl << "t = " << t <<endl;
    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

  • Victor Bazarov

    #2
    Re: Unexpected destructor call

    MirzaD wrote:[color=blue]
    > 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(cha r* string);
    >
    > ~StringClass();
    >
    > StringClass operator=(Strin gClass &string);
    > friend ostream &operator<<(ost ream &output, StringClass &string);
    > };
    > [..][/color]

    The class violates the Rule of Three.

    V

    Comment

    • Alf P. Steinbach

      #3
      Re: Unexpected destructor call

      * MirzaD:[color=blue]
      > 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>[/color]

      Preferentially don't include <iostream> in a header file.
      Use <iosfwd> if necessary (but I'd avoid even that, in general).
      [color=blue]
      > #include <cstring>
      >
      > using namespace std;[/color]

      _Never_ put that in a header file.

      [color=blue]
      > class StringClass{
      > char* s;
      > int size;
      >
      > public:
      > char* name;[/color]

      Don't provide public access to your data members.

      [color=blue]
      > StringClass();
      > StringClass(cha r* string);[/color]

      Should be

      StringClass( char const* string );

      [color=blue]
      >
      > ~StringClass();[/color]

      Look up the FAQ item on The Big Three: you're taking charge of copying,
      and so you need a copy constructor.

      [color=blue]
      >
      > StringClass operator=(Strin gClass &string);[/color]

      Should be

      StringClass& operator=( StringClass const& string );

      [color=blue]
      > friend ostream &operator<<(ost ream &output, StringClass &string);[/color]

      Should be

      friend ostream &operator<<(
      ostream &output, StringClass const& string
      );

      [color=blue]
      > };
      >
      > StringClass::St ringClass(){
      > s = new char[0];
      > size = 0;
      > }[/color]

      Since this is still in the header file, needs to be declared 'inline'.

      [color=blue]
      > StringClass::St ringClass(char* string){
      > size = strlen(string);
      > s = new char[size + 1];
      > strcpy(s,string );
      > }[/color]

      Since this is still in the header file, needs to be declared 'inline'.

      [color=blue]
      > StringClass::~S tringClass(){
      > cout << "Destructor : " << name << endl;
      > delete [] s;
      > }[/color]

      Since this is still in the header file, needs to be declared 'inline'.

      [color=blue]
      > StringClass StringClass::op erator=(StringC lass &string){
      > char* temp;
      > try{
      > temp = new char[string.size + 1];
      > }
      > catch (bad_alloc ex){
      > exit(1);
      > }[/color]

      That's a bit draconian. Let the client code decide what to do with an
      exception.

      [color=blue]
      > strcpy(temp,str ing.s);
      > delete [] s;
      > s = temp;
      > return *this;
      > }[/color]

      Since this is still in the header file, needs to be declared 'inline'.

      [color=blue]
      > ostream &operator<<(ost ream &out, StringClass &string){
      > out << string.s;
      > return out;
      > }[/color]

      Since this is still in the header file, needs to be declared 'inline'.

      [color=blue]
      > //StringClass.cpp
      >
      > #include "StringClas s.h"
      >
      > using namespace std;
      >
      > int main(){
      > StringClass s("test"),t;
      > s.name = "s";
      > t.name = "t";
      > t = s;
      > cout << "s = " << s << endl << "t = " << t <<endl;
      > 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?[/color]

      Your operator= returns a copy, and you haven't defined a copy
      constructor, so invoking the auto-generated copy constructor.

      --
      A: Because it messes up the order in which people normally read text.
      Q: Why is it such a bad thing?
      A: Top-posting.
      Q: What is the most annoying thing on usenet and in e-mail?

      Comment

      • MirzaD

        #4
        Re: Unexpected destructor call

        Adding the copy constructor did solve the problem. Thank you very
        much.

        Mirza

        Comment

        • Jay Nabonne

          #5
          Re: Unexpected destructor call

          On Tue, 29 Nov 2005 12:03:15 -0800, MirzaD wrote:
          [color=blue]
          >
          > StringClass::St ringClass(){
          > s = new char[0];
          > size = 0;
          > }[/color]

          Unrelated to your question, but this code plus the assignment operator
          together cause a bug. The above code does not produce a zero-terminated
          string.
          [color=blue]
          >
          > StringClass StringClass::op erator=(StringC lass &string){
          > char* temp;
          > try{
          > temp = new char[string.size + 1];
          > }
          > catch (bad_alloc ex){
          > exit(1);
          > }
          > strcpy(temp,str ing.s);[/color]

          The above line will fail if string is a default-constructed StringClass
          object, since there will not be a terminating zero.
          [color=blue]
          > delete [] s;
          > s = temp;
          > return *this;
          > }
          >[/color]

          - Jay

          Comment

          • Puppet_Sock

            #6
            Re: Unexpected destructor call

            Alf P. Steinbach wrote:[color=blue]
            > * MirzaD:[/color]
            [snips][color=blue][color=green]
            > > StringClass::~S tringClass(){
            > > cout << "Destructor : " << name << endl;
            > > delete [] s;
            > > }[/color]
            >
            > Since this is still in the header file, needs to be declared 'inline'.[/color]

            What problems does not putting 'inline' here cause?
            Socks

            Comment

            • Victor Bazarov

              #7
              Re: Unexpected destructor call

              Puppet_Sock wrote:[color=blue]
              > Alf P. Steinbach wrote:
              >[color=green]
              >>* MirzaD:[/color]
              >
              > [snips]
              >[color=green][color=darkred]
              >>>StringClass: :~StringClass() {
              >>> cout << "Destructor : " << name << endl;
              >>> delete [] s;
              >>>}[/color]
              >>
              >>Since this is still in the header file, needs to be declared 'inline'.[/color]
              >
              >
              > What problems does not putting 'inline' here cause?[/color]

              A violation of ODR if the header is included in more than one TU.

              V

              Comment

              Working...