Thoughts on C++ copy constructors, the Prototype design pattern and ICloneable interface

by Jason Haley 27. March 2005 12:22

Last week on the train I was reading Design Patterns in C# by Steven John Metsker and came across the Prototype pattern.  A short description of the Prototype patern is this (from the front cover of the book): Provide new objects by copying an example.  So ok, no problem I get that.  As I am reading the chapter and examples, I start thinking about something I was reading the weekend before: C++ copy constructors.  Before I mention anything about C++ I want to remind the reader that I am currently a Beginner (notice the capital 'B') in C++ so I don't know a hell of a lot about it.

C++ copy constructors are used when you want to create an object from an already existing object, such as:

   51 COutline outline2 = outline;

where the “outline” variable is an already existing object.  For those of you who do not use C++, let me try and explain a little of why there is such thing as a copy constructor.  C++ objects have the ability to control (override, overload or implement) about everything that you can do (or would ever want to do) with an object, like operators and even a copy constructor which implements the code that will be run when a new object is to be created by an existing object.  Interesting thought when you think about it. 

.Net has an interface named ICloneable, which (as MSDN says) “Supports cloning, which creates a new instance of a class with the same value as the existing instance”.  So the copy constructor and the ICloneable interface sort of have the same reason for existing.  The only method on the ICloneable interface is Clone() which returns an Object type.  The whole reason I am writing this entry has to do with implementing a prototype pattern ... or should I say creating a new object by copying an “prototype” or “example” object.  Actually since that is not that hard ... the real reason I am writing this is to mention what you need to look out for as well as a little information on how copying objects is handled in C++.  Lets start with the old and work our way to the new ....

C++ copy constructor example:

    4 #include "stdafx.h"

    5 #include <iostream>

    6 using namespace std;

    7  

    8 class COutline

    9 {

   10 private:

   11     int m_index;

   12     char* m_title;

   13 public:

   14     COutline(int index, char* title) {

   15         m_index = index;

   16         m_title = new char [strlen(title)+1];

   17         strcpy(m_title, title);

   18     }

   19  

   20     // copy constructor

   21     COutline(const COutline& outline) {

   22         m_index = outline.m_index;

   23  

   24         m_title = new char [strlen(outline.m_title)+1];

   25         strcpy(m_title, outline.m_title);

   26     }

   27  

   28     int Index()    {

   29         return m_index;

   30     }

   31  

   32     char* Title(){

   33         return m_title; }

   34  

   35     ~COutline()    {

   36         delete[] m_title;

   37     }

   38 };

   39  

   40 int _tmain(int argc, _TCHAR* argv[])

   41 {

   42    

   43     COutline outline(1, "title");

   44  

   45     COutline outline2 = outline;

   46  

   47     cout << outline.Title() << endl;

   48     cout << outline2.Title() << endl;

   49    

   50     return 0;

   51 }

The copy constructor starts on line 21 and goes to line 26.  The whole reason for this copy constructor in this case is to copy the string to a new char array for the new object copy ... otherwise both objects will try to hold references to the same char array, which as I am sure you understand would be a bad thing.  So ok, so that is C++, who cares right?  Well you really need to understand the situation going on here ... there is the same problem in .Net.  Just because we now have a garbage collector, a managed environment and Object.MemberwiseClone() is no excuse for not understanding the differences between values, references and how copies (or clones) are created ... because you might get it wrong if you don't.

Now here is a C# class that I will later show the code that does clone two different ways - shallow copy and deep copy.  This class is an abstract base class that will be used to create the prototype later or implement the prototype pattern how ever you want to look at it.  It is a simple class, an int, a string and an ArrayList.

    5 abstract class Outline : ICloneable

    6 {

    7     private int _index;

    8     private string _title;

    9  

   10     public Outline()

   11     {}

   12  

   13     public Outline(int index, string title)

   14     {

   15         _index = index;

   16         _title = title;

   17     }

   18  

   19     public int Index

   20     {

   21         get{ return _index; }

   22         set{ _index = value; }

   23     }

   24  

   25     public string Title

   26     {

   27         get{ return _title; }

   28         set{ _title = value; }

   29     }

   30  

   31     abstract public object Clone();

   32  

   33     abstract public ArrayList Attributes

   34     { get; }

   35 }

First thing you need to know is that System.Object has a method on it called MemberwiseClone().  Next thing you need to know is that the object copy created from this method is a “Shallow Copy”.  A shallow copy is a new object that has a copy of all the value objects but the reference objects (ArrayList in class above and technically the string - but I won't go into that) have their references copied to this new object.  This gives you the same situation where a copy constructor is needed in C++ (the compiler actually will create a default copy constructor but will only do the shallow copy), the case where you don't want copies of references but new objects and references to those.  So if all you have are value types on the object, MemberwiseClone() would work fine (actually strings will work in this situation too).  Below is a class that implements the above abstract outline class and implements a shallow copy in its Clone() method.

   38 class ConcreteOutline : Outline

   39 {

   40     private ArrayList _attributes;

   41  

   42     public ConcreteOutline(int index, string title) : base (index, title)

   43     {   

   44         _attributes = new ArrayList();

   45         _attributes.Add(index.ToString());

   46         _attributes.Add(title);

   47     }

   48  

   49     override public object Clone()

   50     {

   51         // Shallow copy

   52         return this.MemberwiseClone();

   53     }

   54  

   55     override public ArrayList Attributes

   56     {

   57         get

   58         {

   59             return _attributes;

   60         }

   61     }

   62 }

So if you now run some code like the following, you might get some unexpected results if you don't understand what is going on:

  100         // shallow copy example

  101         ConcreteOutline prototype = new ConcreteOutline(1, "Prototype 1");

  102  

  103         ConcreteOutline coCopy1 = (ConcreteOutline)prototype.Clone();

  104         ConcreteOutline coCopy2 = (ConcreteOutline)prototype.Clone();

  105  

  106         // add an item to the attributes to check if they have references to the same arraylist

  107         coCopy1.Attributes.Add("Add one more");

  108  

  109         // check if the references are equal and check the counts

  110         Console.WriteLine(Object.ReferenceEquals(coCopy1.Attributes,coCopy2.Attributes));  // true

  111         Console.WriteLine(coCopy1.Attributes.Count);  // 3

  112         Console.WriteLine(coCopy2.Attributes.Count);  // 3

Since our new object is a shallow copy of the prototype ... the ArrayList for the first object is the same one the second object points to.  This is why if you add items to one list, you get it added to the second list ... or actually the same exact list!

In the code below, I try to create a sort of private copy constructor (though I'm not 100% sure it makes since in this case) that aids the Clone() method in returning a deep copy of the prototype.

   64 class ConcreteOutline2 : Outline

   65 {

   66     private ArrayList _attributes;

   67  

   68     public ConcreteOutline2(int index, string title) : base (index, title)

   69     {

   70         _attributes = new ArrayList();

   71         _attributes.Add(index.ToString());

   72         _attributes.Add(title);

   73     }

   74  

   75     private ConcreteOutline2(ConcreteOutline2 concreteOutline)  : base (concreteOutline.Index, concreteOutline.Title)

   76     {

   77         this._attributes = (ArrayList)concreteOutline.Attributes.Clone();;

   78     }

   79  

   80     public override ArrayList Attributes

   81     {

   82         get

   83         {

   84             return _attributes;

   85         }

   86     }

   87  

   88     override public object Clone()

   89     {

   90         // Deep copy

   91         return new ConcreteOutline2(this);

   92     }

   93 }

So now if you run some code like below, you will get two separate ArrayLists for the two seperate objects:

  114         // deep copy example

  115         ConcreteOutline2 prototype2 = new ConcreteOutline2(2, "Prototype 2");

  116         ConcreteOutline2 co2Copy1 = (ConcreteOutline2)prototype2.Clone();

  117         ConcreteOutline2 co2Copy2 = (ConcreteOutline2)prototype2.Clone();

  118        

  119         // add an item to the attributes to check if they have references to the same arraylist

  120         co2Copy1.Attributes.Add("Add one more");

  121        

  122         // check if the references are equal and check the counts

  123         Console.WriteLine(Object.ReferenceEquals(co2Copy1.Attributes,co2Copy2.Attributes));  // false

  124         Console.WriteLine(co2Copy1.Attributes.Count); // 3

  125         Console.WriteLine(co2Copy2.Attributes.Count); // 2

So what does this all mean?  Well it means you need to be careful when you are copying objects.  You need to make sure (like C++ programmers did before you) that you are indeed creating the type of copy you need, either shallow or deep.

Some links for you to check out:

C++ Notes: OOP: Copy Constructors http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
Data & Object Factory: Prototype Pattern: http://www.dofactory.com/Patterns/PatternPrototype.aspx
C# Corner: Prototype Pattern http://www.c-sharpcorner.com/Code/2002/Mar/PrototypePattersAJ.asp

Comments (3) | Post RSSRSS comment feed |

Categories:
Tags:

Comments

Comments are closed