The destructor of the class is called after instantiating object with its copy constructor

2

It is happening to me that when executing the copy constructor of the String class, immediately after instantiating my object, the destructor of the class is called, and it eliminates the newly created object. Why does this happen? How could he avoid it?

using namespace std;

class String{

private:
  char *str;

protected:
  char* takeMemory(const char *s);

public:
  String(const char *str = " ");
  ~String(void);
  String(const String &obj);
  friend istream& operator >> (istream &input, String &obj);
  friend ostream& operator << (ostream &input, const String &obj);
};

 #include <iostream>
 #include "StringsClass.h"
 using namespace std;

 int main()
 {
   String str = String();
   String str2;
   cout << "\t\t\t Test Clase string" << endl;
   cout<<str<<endl;
   cin>>str;
   cout<<str<<endl;
   str2 = String(str);
   cout<<str2<<endl;
   return 0;
 }


 String::~String(void){
   delete []str;
   str = NULL;
   cout<<"Destruyendo Objeto str con direccion de memoria "<<this<<endl;
 }

 String::String(const String &obj):str(takeMemory(obj.str)){
   cout<<"Ejecutando constructor de copia"<<endl;
   if(!(this->str) && !obj.str)
       cerr<< "ERROR - Sin memoria"<< endl;
 }

 char* String::takeMemory(const char *s){
   if(s && *s){
    try{
        char *aux = new char[strlen(s) + 1];
        return strcpy(aux,s);
    }catch(std::bad_alloc &){
        //TODO: Tratar exepcion
    }
 }
 return NULL;
}

    
asked by Jorge Gonzalez 22.11.2016 в 14:05
source

1 answer

5
   str2 = String(str);

Let's analyze this line. What happens here is the following:

  • A temporary object is created that will be a copy of str (hence the copy constructor is called).
  • The assignment operator is then called, in charge of copying the information of said temporary object to str2 .
  • Finally the temporary object is destroyed since the execution leaves its scope.

An example to make it look a little clearer:

class String{
public:
  String(const char *str = " ")
  { std::cout << "String::String(const char*)\n"; }

  ~String()
  {
    std::cout << "String::~String()\n";
  }

  String(const String &obj)
  {
    std::cout << "String::String(String)\n";
  }

  String& operator=(const String& obj)
  {
    std::cout << "String::operator=(String)\n";
    return *this;
  }

  friend istream& operator >> (istream &input, String &obj);
  friend ostream& operator << (ostream &input, const String &obj);
};

int main()
{
  std::cout << "Creacion objeto str\n";
  String str = String();
  std::cout << "Creacion objeto str2\n";
  String str2(str);
  std::cout << "Creacion objeto str3 (copia temporal)\n";
  String str3;
  str3 = String(str);
  std::cout << "Asignacion str2=str\n";
  str2 = str;
  std::cout << "Liberacion de recursos\n";
  return 0;
}

The output of this example is as follows:

Creacion objeto str
    String::String(const char*)
Creacion objeto str2
    String::String(String)
Creacion objeto str3 (copia temporal)
    String::String(const char*)
    String::String(String)
    String::operator=(String)
    String::~String()
Asignacion str2=str
    String::operator=(String)
Liberacion de recursos
    String::~String()
    String::~String()
    String::~String()

That explained in detail would look like this:

Creacion objeto str
    String::String(const char*) // 1
Creacion objeto str2
    String::String(String) // 2
Creacion objeto str3 (copia temporal)
    String::String(const char*) // 3
    String::String(String) // 4
    String::operator=(String) // 5
    String::~String() // 6
Asignacion str2=str
    String::operator=(String) // 7
Liberacion de recursos
    String::~String() // 8
    String::~String() // 9
    String::~String() // 10
  • str is created. This constructor is called because it has values because, despite having an argument, the declaration is assigned one by default. For this reason it replaces the default constructor
  • The copy constructor is called to create str2
  • The str3 object is created. Same case as in (1)
  • The temporary object is created from str
  • The content of the temporary object is copied to str3
  • The temporary object is destroyed
  • Copy of the content of str in str2
  • Destruction of str3
  • Destruction of str2
  • Destruction of str
  • EDITO :

    Why is not the constructor copied from str2 directly from the line that opens this answer?

    The reason is that, in that line, str2 has already been built and C ++ will not call the constructor copy on its own (among other things because that could lead to stability problems when leaving resources without releasing ).

    Having said that, in C ++ there are two ways to call the copy constructor:

    String str2(str);
    String str2 = String(str);
    

    Being, in this case, the second option is more recommendable since the first one could be confused with the declaration of a function. In fact in C ++ 11 the correct thing would be to do the following:

    String str2{str};
    

    But beware, the following code is not going to call the copy constructor that we expect, but will involve the creation of a temporary object:

    String str2;
    str2 = String(str);
    

    In this case, the ideal thing would be to do the following:

    String str2;
    str2 = str;
    

    Because this way, at least, we avoid a construction and an erasure.

        
    answered by 22.11.2016 / 15:02
    source