Last Updated:

ICloneable Interface | Clone | Example | C#

Sometimes it is necessary to make a copy of the object. If this copies objects that contain other objects or pointers to them, then for the correct implementation of this process, it is necessary to understand its features. Below we will compare pointer copying, superficial padded copying and detailed copying. We will see that depending on the implementation of the ICloneable interface used to copy objects, there is the possibility of both surface and detailed copying.

 

Recall that the .NET Framework has meaningful data types and reference types. Meaningful data types represent the data itself, and reference types merely indicate the location of the data. If you copy the value of one pointer variable to another variable of the same type, both variables point to the same object. If you change this object by using the first pointer, the object referenced by the second pointer will also change. Sometimes this behavior is required, and sometimes another.

ICloneable Interface

 

The ICloneable interface is the original one and has a single method, Clone. This method can be implemented to perform both surface and detailed copying, but the pointer to the Object returned by the method must point to an object of the same (or compatible) type for which the ICloneable interface is implemented. Typically, the Clone method is implemented so that it creates a new object. However, it is the case, for example in the case of the String class, that the Clone method simply returns a pointer to the original object.

_gc_interface ICloneable
// garbage collector - ICloneable
{
Object* Clone(); // Clone
};

Superficial and detailed copies

Unmanaged structures and classes in C++ automatically implement chunked copying of content that will be relevant until the copy constructor is spoofed. Black copying is also called superficial copying. The Object base class also has a protected method, MemberwiseClone, that performs a checked copy of managed structures or classes.

If there are pointers among the members of a structure or class, such a chunked copy may not be what you need. The result of this copying is that pointers to different objects will refer to the same data, not to independent copies of it. For the actual copying of the data, a detailed copy should be made. Detailed copying can be provided at the language level or at the library level. In normal C++, verbose copying is done at the language level using the copy constructor. In managed C++, it is implemented by the .NET Framework using the Idoneable interface, which can be implemented in the classes that you create specifically so that these classes can perform detailed copying.

The following three options exhaust the possible types of copying, both for managed classes and structures, with or without the Idoneable interface, and for unmanaged classes and structures.

  • Assigning the value of one pointer to another (whether pointers to managed or unmanaged classes or structures) causes pointers to simply be copied. This is not a superficial or detailed copying, but a simple assignment of pointers.
  • Converting one non-reference (non-referenced) type to another, and these types can be unmanaged classes, unmanaged structures, or meaningful data types, is a superficial copy automatically performed by C++.
  • Assigning a new pointer a value returned by the Clone method (of course, this value must also be a pointer) that is a managed class or structure with the ICloneable interface implemented is a superficial or verbose copy, depending on the implementation of the Clone method.

 

Sample program

Let's illustrate the material with copyDemo. This utility makes a copy of an instance of the Course class. The Course class contains the name of the course and the list (collection) of students.

//Course.h
_gc class Course: public Idoneable
// garbage collector class Course: Idoneable
{
public:
String *pTitle;
ArrayList *pRoster;
Course(String *pTitle) // Course
{
this › pTitle = pTitle; pRoster = new ArrayList;
}
void AddStudent(String *pName)
{
pRoster › Add(pName); // Add
}
void Show(String *pCaption) // Show
{
Console::WriteLine("– {0}– ", pCaption);
Console::WriteLine(
"Course: {0} with {1} students", // Course: students
title,
_box(pRoster › Count)); // Check
lEnumerator *pEnum = pRoster › GetEnumerator();
while (pEnum › MoveNext())
{
String *pName =
dynamic_cast<String *>(pEnum › Current);
Console::WriteLine(pName);
}
}
Course *ShallowCopy() // The course is a shallow copy
{
return dynamic_cast<Course *>(MemberwiseClone());
}
Object *Clone()
{
Course *pCourse = new Course(pTitle); // new Course
pCourse › pRoster =
dynamic_cast<ArrayList *>(pRoster › Clone()); // Clone
return pCourse;
}
};

The test program creates an instance of the Course class called pC1 and then creates copies of it named pC2 in a variety of ways.

//CopyDemo.h
_gc class CopyDemo
// garbage collector class CopyDemo
{
private: // private
static Course *pCl, *pC2; // static Heading
public:
static void Main()
{
Console::WriteLine("Copy is done via pC2 = pCl");
// Copy made via pC2 = pCl
InitializeCourse();
pCl › Show("original"); // Show("original");
pC2=pCl;
pC2 › Show("copy"); // Show("copy");
pC2 › pTitle = ".NET Programming"; // .NET programming
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student");
// Show("copy with changed name
//and a new student");
pCl › Show("original"); // Show("original");
Console::WriteLine(
"\nCopy is done via pC2 = pCl › ShallowCopy()");
InitializeCourse();
pC2 = pCl › ShallowCopy();
pC2 › pTitle = ".NET Programming"; // .NET programming
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student"};
// Show("copy with changed name and new
// student");
pCl › Show("original"); // Show("original");
Console::WriteLine(
"\nCopy is done via pC2 = pCl › Clone()");
InitializeCourse();
pC2 = dynamic_oast<Course *>(pCl › Clone());
pC2 › pTitle = ".NET Programming"; // programming
//on .NET
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student");
// Show("copy with changed name
// new student");
pCl › Show("original"); // Show("original");
}
private: // private
static void InitializeCourse()
{
pCl = new Course("Intro to Managed C++");
// new Course("Introduction to Managed C++");
pCl › AddStudent("John"); // John
pCl › AddStudent("Mary"); // Mary
}
};

Here is the issuance of the program:

Copy is done via pC2 = pC1
– original-----
Course: Intro to Managed C++ with 2 students
John
Mary
– copy–
Course: Intro to Managed C++ with 2 students
John
Mary
– ----copy with changed title and new student-----
Course: .NET Programming with 3 students
John
Mary
Charlie
– --original-----
Course: .NET Programming with 3 students
John
Mary
Charlie
Copy is done via pC2 = pCl › ShallowCopy()
– copy with changed title and new student--– -
Course: .NET Programming with 3 students
John
Mary
Charlie
– --original-----
Course: Intro to Managed C++ with 3 students
John
Mary
Charlie
Copy is done via pC2 = pCl › Clone()
– -copy with changed title and new student– --
Course: .NET Programming with 3 students
John
Mary
Charlie
– original –
Course: Intro to Managed C++ with 2 students
John
Mary

Issue: #1

Copy made via pC2 = pC1
– --original-----
Course: Introduction to Managed C++ with 2 students
John
Mary
– --copy–
Course: Introduction to Managed C++ with 2 students
John
Mary
– --copy with changed name and new student-----
Course: .NET Programming with 3 students
John
Mary
Charlie
– original–
Course: .NET Programming with 3 students
John
Mary
Charlie
Copy made via pC2 = pC1 › ShallowCopy()
– --copy with changed name and new student-----
Course: .NET Programming with 3 students
John
Mary
Charlie
– -original---–

Course: Introduction to Managed C++ with 3 students
John
Mary
Charlie
Copy made via pC2 = pC1 › Clone()
– retitled copy with new student– ---
Course: .NET Programming with 3 students
John
Mary
Charlie
– --original-----
Course: Introduction to Managed C++ with 2 students
John
Mary

 

Copy pointers by assigning

The first method of copying is simply assigning pC2 = pC1. In this case, we get two pointers to the same object, and the changes made using the first pointer can be detected in the data addressed by the second pointer.

_gc class CopyDemo
// garbage collector class CopyDemo
{
public:
static void Main()
{
Console::WriteLine("Copy is done via pC2 = pCl");
// Copy made via pC2 = pCl
InitializeCourse();
pC1 › Show("original"); // Show("original");
pC2=pCl;
pC2 › Show("copy"); // Show("copy");
pC2 › pTitle = ".NET Programming"; // .NET programming
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student"};
// Show("copy with changed name
//and a new student");
pC1 › Show("original"); // Show("original");

An instance of the Course class is initialized by the InitializeCourse method, with "Intro to Managed C++" as the course name and two students enrolled in the course. Then the assignment pC2 = pC1 is performed, and the pC2 pointer changes the title and adds a new student. It is then demonstrated that the changes made are visible using both pointers. Here is the result of the first part of the program:

Copy is done via pC2 = pC1
--original-----
Course: Intro to Managed C++ with 2 students
John
Mary
– copy-– -
Course: Intro to Managed C++ with 2 students
John
Mary
– copy with changed title and new student-----
Course: .NET Programming with 3 students
John
Mary
Charlie
– --original-----
Course: .NET Programming with 3 students
John
Mary
Charlie

Issue: #2

Copy made via pC2 = pC1
– --original--–
Course: Introduction to Managed C++ with 2 students
John
Mary
– --copy-----
Course: Introduction to Managed C++ with 2 students
John
Mary
– --copy with changed name and new student-----
Course: .NET Programming with 3 students
John
Mary
Charlie
– --original-----
Course: .NET Programming with 3 students
John
Mary
Charlie

 

Checked copying

Now let's illustrate the checked copy performed using the MemberwiseClone method of the Object class. Because this method is secure (has the protected access specifier), it can only be called directly from an instance of the Course class. Therefore, in the Course class, we defined a ShallowCopy method implemented through the MemberwiseClone method.

_gc class Course: public ICloneable
// garbage collector class Course: ICloneable
{
Course *ShallowCopy()
{
return dynamic_cast<Course *>(MemberwiseClone());
}
};

Here is the second part of the test program, in which the ShallowCopy method is called. Just as before, use the second index to change the title and add a new student to the list.

_gc class CopyDemo
// garbage collector class CopyDemo
{
public:
static void Main() {
Console::WriteLine(
"\nCopy is done via pC2 = pCl › ShallowCopy()");
InitializeCourse();
pC2 = pCl › ShallowCopy();
pC2 › pTitle = ".NET Programming"; // .NET programming
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student");
// Show("copy with changed name
// and a new student");
pCl › Show("original");
// Show("original");

Below is the result of the second part of the program. You can see that the Title field exists after being copied in two independent instances, but the Roster collection, which is a list of students, was copied through the index. Therefore, it is the same for both copies, and changes made using one pointer are visible through the other.

Copy is done via pC2 = pCl › ShallowCopy()
– -copy with changed title and new student– -–
Course: .NET Programming with 3 students
John
Mary
Charlie
– ---original-----
Course: Intro to Managed C++ with 3 students
John
Mary
Charlie

Issue: # 03

Copy made via pC2 = pC1 › ShallowCopy()
– ---copy with changed name and new student-----
– Course: .MET Programming with 3 students John
Mary
Charlie
– --original-----
Course: Introduction to Managed C++ with 3 students
John
Mary
Charlie

Using ICloneable

The last copy method takes advantage of the fact that the Course class supports the ICloneable interface and implements the Clone method. To copy the Roster collection, you also use the fact that the ArrayList implements the ICloneable interface, as mentioned earlier in this chapter. Note that the Clone method returns an Object* value, so you must cast the return value to the ArrayList* type before you can assign it to the pRoster field.

_gc class Course: public ICloneable
// garbage collector class Course: ICloneable
{
public:
Object *Clone()
{
Course *pCourse = new Course(pTitle); // new course
pCourse › pRoster =
dynamic_cast<ArrayList *>(pRoster › Clone());
return pCourse;
}
};

Here is the third part of the program, in which the Clone method is called. Here, too, we use the second index to change the name and add a new student to the list.

_gc class CopyDemo
// garbage collector class CopyDemo
{
public:
static void Main()
{
Console::WriteLine(
"\nCopy is done via pC2 = pCl › Clone()");
InitializeCourse();
pC2 = dynamic_cast<Course *>(pCl › Clone());
pC2 › pTitle = ".NET Programming"; // .NET programming
pC2 › AddStudent("Charlie"); // Charlie
pC2 › Show("copy with changed title and new student");
// Show("copy with changed name
//and a new student");
pCl › Shcw("01iginal");
// Show("original");

Here is the issuance of the third part of the program. Now you can see that as a result of copying, we got two independent instances of the Course class, each of which has its own title and a list of students.

Copy is done via рС2 = pCl › Clone()
– ---copy with changed title and new student-– --
Course: .NET Programming with 3 students
John
Mary
Charlie
– ---original--–
Course: Intro to Managed C++ with 2 students
John
Mary

Issue: # 04

Copy made via pC2 = pC1 › Clone O
– ---copy with changed name and new student-– --
Course: .NET Programming with 3 students
John
Mary
Charlie
– ----original-----
Course: Introduction to Managed C++ with 2 students
John
Mary

The latter approach illustrates the nature of .NET interfaces. You can copy objects without really thinking or worrying about their types.