I have some confusion about Copy Constructor.

Why this program crashes if we remove Copy Constructor MyString?

#include <iostream>
#include <cstring>

using namespace std;

class MyString
{
private:
   char* Buffer;

public:
   // constructor
   MyString(const char* InitialInput)
   {
      cout << "Default constructor: creating new MyString" << endl;
      if(InitialInput != NULL)
      {
         Buffer = new char [strlen(InitialInput) + 1];
         strcpy(Buffer, InitialInput);

         // Display memory address pointed by local buffer
         cout << "Buffer points to: 0x" << hex;
         cout << (unsigned int*)Buffer << endl;
      }
      else 
         Buffer = NULL;
   }

   // Copy constructor
   MyString(const MyString& CopySource)
   {
      cout << "Copy constructor: copying from MyString" << endl;

      if(CopySource.Buffer != NULL)
      {
         // ensure deep copy by first allocating own buffer 
         Buffer = new char [strlen(CopySource.Buffer) + 1];

         // copy from the source into local buffer
         strcpy(Buffer, CopySource.Buffer);

         // Display memory address pointed by local buffer
         cout << "Buffer points to: 0x" << hex;
         cout << (unsigned int*)Buffer << endl;
      }
      else 
         Buffer = NULL;
   }

   // Destructor
   ~MyString()
   {
      cout << "Invoking destructor, clearing up" << endl;
      if (Buffer != NULL)
         delete [] Buffer;
   }

   int GetLength() 
   {
      return strlen(Buffer);
   }

   const char* GetString()
   {
      return Buffer;
   }
};

void UseMyString(MyString Input)
{
   cout << "String buffer in MyString is " << Input.GetLength();
   cout << " characters long" << endl;

   cout << "Buffer contains: " << Input.GetString() << endl;
   return;
}

int main()
{
   MyString SayHello("Hello");

   UseMyString(SayHello);

   return 0;
}

If you don't write a copy constructor, you get one for free anyway. A very basic and simple one. In this case, all it will do is copy the pointer, Buffer.

Let's think about what happens when you don't provide the copy constructor.

First, a MyString object is created. This is a simple object; basically a pointer, pointing at some memory (that memory was requested in your ordinary constructor).

Then, you create another MyString object. With no copy constructor written, you get the simple one (which just copies the pointer - it doesn't do anything involving memory, or what the pointer might be pointing at). So now you have two MyString objects.

The first one contains a pointer, pointing at some memory.
The second one contains a pointer with the exact same value in it (because you got the simple copy constructor for free, which just makes an exact copy of the pointer value) so it is pointing to the exact same memory.

Then, one of them is destructed, and delete [] Buffer; is called. Buffer is that pointer, and it's pointing at some memory.

Then, the second one is destructed, and delete [] Buffer; is called. It's a different pointer, but it's pointing at the exact same memory. So you have now called delete[] twice on the same memory. This is very, very bad. It's forbidden.

Your copy constructor solves this problem by ensuring that the new MyString object's pointer is pointing at different memory.

Edited 1 Year Ago by Moschops

Comments
Nicely explained.

Good explanation Moschops. Now on to nit-picking and some common new C++ programmer errors.

  1. Always initialize member variables that do not have default constructors, such as pointers. See code below.

  2. DO NOT use NULL for null pointers in C++. Always use 0 (as shown below). NULL is usually defined as a void* and if your compiler is set to fail on warnings, or set to observe strict C++ standards, then using NULL as you did will cause a compiler error since your pointers are char* types and not void* types. You cannot (or should not) expect an automatic cast of a pointer from void to char. The C++ standards allow, and encourage, the use of 0 to initialize or set any pointer type.

    MyString(const char* InitialInput) : Buffer(0)
    {
        cout << "Default constructor: creating new MyString" << endl;
        if(InitialInput != 0)
        {
            Buffer = new char [strlen(InitialInput) + 1];
            strcpy(Buffer, InitialInput);
            // Display memory address pointed by local buffer
            cout << "Buffer points to: 0x" << hex;
            cout << (unsigned int*)Buffer << endl;
        }
    }
    
    // Copy constructor
    MyString(const MyString& CopySource) : Buffer(0)
    {
        cout << "Copy constructor: copying from MyString" << endl;
        if(CopySource.Buffer != 0)
        {
            // ensure deep copy by first allocating own buffer
            Buffer = new char [strlen(CopySource.Buffer) + 1];
            // copy from the source into local buffer
            strcpy(Buffer, CopySource.Buffer);
            // Display memory address pointed by local buffer
            cout << "Buffer points to: 0x" << hex;
            cout << (unsigned int*)Buffer << endl;
        }
    }
    

There are a few more nits, but we will leave them alone for now. :-)

Edited 1 Year Ago by rubberman

I have to add that if you have a C++ 11 compiler then you should be using nullptr.

Comments
Which is probably #define'd to 0... :-)

Thanks for your answers guys, i really appreciate it.

This question has already been answered. Start a new discussion instead.