Jump to content
  • Advertisement
Sign in to follow this  

When and When not to use Copy Constructors?

This topic is 4396 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

After posting a few topics and reading some stuff on the internet... I still couldn't get a full grasp of copy constructors... Of course, I understood a few... Or maybe more... I just wanted everything to be clear to me and also for those poeple who still don't understand... So, I'm asking in my preferred way... Please enumerate when and when not to use copy constructors... When 1. 2. 3. or more... when not 1. 2. 3. or more... Thank you for your inputs...

Share this post


Link to post
Share on other sites
Advertisement
Hey bud,

There are two types of copying of an object, shallow and deep.

Shallow:

This is where the object is copied, not any of the data that may be pointed to by any of its members.

Deep:

This is where the data that may be pointed to by any of its members is copied as well.

The difference being that the shallow copy member pointers point to the same memory as the one copied froms members, if that makes sense. With the deep copy, the pointers have the memory allocated for them, so there are also two copies of the memories being pointed to.

Now the deep copy isnt automatic, the reason you write a copy constructor is so that you can do this yourself.

Another thing to note is that you typically overload the operator=() as well. Since the copy constructor is used here. One other thing to note is that the best way to do a shallow copy is to make a local temporary equal the reference and swap this for the temporary, like this:

MyClass( const MyClass& other )
{
MyClass temp = other;

std::swap( this, temp );
}

This way your old this cleans up as well.

Hope that helps,

Dave

Share this post


Link to post
Share on other sites
To add to what Dave said, always remember the rule of 3: If you need a copy constructor, assignment operator (operator=), or destructor, you probably need all three.

Share this post


Link to post
Share on other sites
I'm pretty new to c++, but it just so happens that I'm working on this particular area as well. From my understanding there are two prerequisites to overriding the copy ctor and assignmnet overload operator.

1) You're using dynamic data (new keyword)
2) You're assigning object variables from one object to another
3) Passing byVal

From my limited understanding basically if you allow a shallow copy to be done then you're actually creating another object (non temp). When your destructor calls the delete you'll delete the original object but the copied new object gets left in memory. By overwriting these default shallow copies you're forcing a deep copy and instead of creating an entirely new object you're just assigning a new pointer reference to the already initiated object (via a temp object).

I openly admit now that this could quite possibly be misinformation, but from my understanding it's how it breaks down. To give you an example of what happens when you fail to overwrite:


// DynamicClass.h -- bad technique
#include <iostream>
#ifndef DYNAMICCLASS_H
#define DYNAMICCLASS_H

class DynamicClass
{
private:
char * dynString;
int length;
static int count;
public:
DynamicClass(const char *s);
DynamicClass();
~DynamicClass();
friend ostream &operator<<(ostream &stream, const DynamicClass &s);
};
#endif




DynamicClass.cpp


//DynamicClass.cpp

#include <iostream>
#include <cstring>
#include "DynamicClass.h"

using namespace std;

int DynamicClass::count = 0;

DynamicClass::DynamicClass(const char *s)
{
length = strlen(s);
dynString = new char[length + 1]; //dynamic data
strcpy(dynString, s);
count++;
cout << count << ": \"" << dynString
<< "\ " ctor called\n";
}

DynamicClass::DynamicClass()
{
length = 1; //sets to 1 because of \0.
dynString = new char[1];
strcpy(dynString, "");
count++;
cout << count << ": \"" << "\"\" default ctor called\n";
}

DynamicClass::~DynamicClass()
{
cout << "\ " << dynString << "\ " destructor called, object deleted, ";
--count;
cout << count << " DynamicClass objects left\n";
delete [] dynString;
}

ostream & operator<<(ostream &os, const DynamicClass &s)
{
os << s.dynString;
return os;
}





Test.cpp


// Test.cpp

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

void passByRef(dynString &s);
void passByVal(dynString s);

int main()
{
dynString test1("Hello World");
dynString test2("Goodbye World");
dynString test3("Cruel Cruel World");
cout << "test1: " << test1 << endl;
cout << "test2: " << test2 << endl;
cout << "test3: " << test3 << endl;
passByRef(test1);
cout << "test1: " << test1 << endl;
passbyVal(test2);
cout << "test2: " << test2 << endl;
cout << "Calling our default copy ctor:\n";
dynString test4;
test4 = test1;
cout << "test4: " << test4 << endl;
cout << "End Test";
system("Pause");
return 0;
}

void passByRef(dynString &s)
{
cout << "String passed by reference:\n";
cout << "\t\"" << s << "\"\n";
}

void passByVal(dynString s)
{
cout << "String passed by value:\n";
cout <<\t\"" << s << "\"\n";
}





You'll notice that when this is run everything gets completely screwed up. Our object count is hosed and passing by value called our destructor, and didn't prevent our object from being changed.

The reason for this bizarre happening is that when the copy ctor was called the shallow copy created a new object. Because we don't have a copy ctor defined our static count field never gets gets incremented in those instances. This same thing occurs when we pass references by value. We're creating an entire new object so that we ensure the original object isn't altered. Now we have two new objects floating around that our class doesn't 'know' about. The destructor deletes these objects as they were called hence the reason the output of the -1 count and -2 count. The destructor is decrementing our static count, but the 2 objects created by the default ctor were never incremented.

While this is pretty convulted if you walk through it a few times you should be able to see what exactly is going on. I ran through this program probably 10 or 15 times playing around with adding my own copy ctors trying to figure out exactly when and where things occurred.

It should be mentioned that the concept of this program came from C++ Primer Plus 4th edition. I don't take credit for the concept however I did try to change it as much as possible. I hope it helps a little in understanding this concept, I'm personally still not 100% confident in my ability using it, but I suspect that comes with time and practice

[Edited by - senelebe on May 7, 2006 11:11:23 PM]

Share this post


Link to post
Share on other sites
Thanks all for you replies. I'll go read your posts once I get back from my job interview. Thanks! ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!