Jump to content
  • Advertisement
Sign in to follow this  
Decrius

[C++] A valid class - copy constructors / assignment operators?

This topic is 3674 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

Hi, #1 What would be the correct way of writing a class? What functions _must_ be in the class except for the (de-)constructor? #2 Also, shouldn't a copy constructor and an assignment operator do the same thing (in my case, reallocating dynamic allocations for example...what else are they usefull for beside that?)? I mean, they both should make sure the values are copied from one to another object safely. Would this be alright (no assignment operator is defined explicitly):
Component::Component(Component const &instance)
{
    *this = instance;
}
#3 What if the copy constructor would change values of the original (the one which is copied and pasted into the current (copy constructor holding) class) object? Should I really cast it and call a non-const type parameter version of the copy constructor? And yes, my project is supposed to be release in the future, trying to make it use valid C++... Thanks :-)

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Decrius
#1 What would be the correct way of writing a class? What functions _must_ be in the class except for the (de-)constructor?


Any constructors, destructors, and the operators [], (), =, and type-casting operators. Plus any function that needs access to the private members of exactly one instance of your class.

Quote:
#2 Also, shouldn't a copy constructor and an assignment operator do the same thing (in my case, reallocating dynamic allocations for example...what else are they usefull for beside that?)? I mean, they both should make sure the values are copied from one to another object safely.

Would this be alright (no assignment operator is defined explicitly):


No, they shouldn't. A copy constructor constructs (it initializes data) whereas an assignment operator modifies (it assigns to existing data). By the way, it is usually preferable to implement the assignment operator in terms of copy-and-swap, rather than implement the copy constructor in terms of assignment. This is both a performance tip and a necessity when there are no default constructors for some of your members.

Quote:
#3 What if the copy constructor would change values of the original (the one which is copied and pasted into the current (copy constructor holding) class) object? Should I really cast it and call a non-const type parameter version of the copy constructor?


The copy constructor must not change the original, otherwise it's not a copy constructor anymore. What are you trying to do?

Share this post


Link to post
Share on other sites
Original post by Decrius
Hi,
Quote:

#1 What would be the correct way of writing a class? What functions _must_ be in the class except for the (de-)constructor?

Theoretically none, for example the following is valid as the compiler will generate(special functions) a constructor, copy constructor, assignment operator and a destructor.

class foo{};

int main()
{
foo f;
foo g(f);
g = f;
}


Quote:

#2 Also, shouldn't a copy constructor and an assignment operator do the same thing (in my case, reallocating dynamic allocations for example...what else are they usefull for beside that?)? I mean, they both should make sure the values are copied from one to another object safely.

The assignment operator should also check for self assignment, in your case you may well delete dynamic memory and then try to copy the memory just deleted.
Quote:

*** Source Snippet Removed ***
Would this be alright (no assignment operator is defined explicitly):

Again in your case no, as this would be a shallow copy and you require a deep copy, the general rule of thumb is if you define one of the special functions define all three of them.

Quote:

#3 What if the copy constructor would change values of the original (the one which is copied and pasted into the current (copy constructor holding) class) object? Should I really cast it and call a non-const type parameter version of the copy constructor?

Why would a copy constructor be changing the input parameter? Casting a constant object into a non constant object can lead to undefined behaviour if the object was declared constant at the time of construction.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
The copy constructor must not change the original, otherwise it's not a copy constructor anymore.

Doesn't std::auto_ptr do just that?

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Any constructors, destructors, and the operators [], (), =, and type-casting operators. Plus any function that needs access to the private members of exactly one instance of your class.


Ah okay, thanks. Does it matter that one of my classes has a custom way of handeling the [] operator? The use of it make the code look better (imo), I can aswell delete it though...

Quote:
Original post by ToohrVyk
No, they shouldn't. A copy constructor constructs (it initializes data) whereas an assignment operator modifies (it assigns to existing data). By the way, it is usually preferable to implement the assignment operator in terms of copy-and-swap, rather than implement the copy constructor in terms of assignment. This is both a performance tip and a necessity when there are no default constructors for some of your members.


Ah, so with a copy constructor I should reallocate the dynamic memory, and with the assignment operator I would simply pass the pointer so they both point to the same dynamic memory?

Quote:
Original post by ToohrVyk
The copy constructor must not change the original, otherwise it's not a copy constructor anymore. What are you trying to do?


I thought out a design (for memory management) which required it. I've read things about reference counting and seems to fit better. I can aswell use ptr libraries from Boost which you suggested, but I felt that adding as little dependencies (other libraries) to a library would be best...if it gets too complicated though I would surely use the Boost library...most people know or have Boost anyways.

One more question, the copy constructor and assignment operator: should I, if I make them, copy all static (on the stack) memory myself? Why would a compiler-made assignment operator be worse then a self-made one, since I guess that if I copy all values I do the same thing as a compiler-made one...

And, would the copy constructor and assignment operator be practically the same for data that is on the stack? Or am I missing the point somewhere?

Thanks :)

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
Quote:
Original post by ToohrVyk
The copy constructor must not change the original, otherwise it's not a copy constructor anymore.

Doesn't std::auto_ptr do just that?


That's why it's not called "copy constructor" in std::auto_ptr's case.

Quote:
Original post by Decrius
#2 Also, shouldn't a copy constructor and an assignment operator do the same thing


The difference is that copy constructor initializes an object based from an already-existing one -- an assignment operator runs on an already constructed and initialized object and sets its new value.

Share this post


Link to post
Share on other sites
Quote:
Original post by Decrius
#1 What would be the correct way of writing a class? What functions _must_ be in the class except for the (de-)constructor?

The best answer for this is an example of a valid class.

class Class { };

Note, however, the compiler will supply default contructors, destructors, assignment operators, and static new and delete functions.
Quote:

#2 Also, shouldn't a copy constructor and an assignment operator do the same thing (in my case, reallocating dynamic allocations for example...what else are they usefull for beside that?)? I mean, they both should make sure the values are copied from one to another object safely.

Effectively, yes. It is good practice to implement the assignment operator in terms of the copy constructor in order to avoid code duplication.

class C
{
public:
C(const C& rhs)
: m_i(rhs.m_i)
{ }

C& operator=(const C& rhs)
{
C tmp(rhs);
return swap(tmp);
}

private:
C& swap(C& rhs)
{
std::swap(m_i, rhs.m_i);
return *this;
}
private:
int m_i;
};

Quote:
Would this be alright (no assignment operator is defined explicitly):

No, it's unlikely to compile because instance is const and you're trying to assign to a non-const.
Quote:
#3 What if the copy constructor would change values of the original (the one which is copied and pasted into the current (copy constructor holding) class) object?

It's possible to do this: it's how std::auto_ptr is implemented, and it's often used wit smart pointers. The argument to the copy constructor does not have to be a const reference, it just has to be a reference.

Modifying the source object during a copy is a dark, damp corner of language filled with unspeakable lurking horrors that enjoy eating dead, burnt babies feeling the veins between their teeth. You would do well to avoid such places unless you really really really know what you are doing. You have now been warned.

Share this post


Link to post
Share on other sites
Quote:
Original post by Oxyd
The difference is that copy constructor initializes an object based from an already-existing one -- an assignment operator runs on an already constructed and initialized object and sets its new value.


Ah yes, I get the point now...forgot that the copy constructor was only used for initialisation of new objects.

Share this post


Link to post
Share on other sites
Quote:
Original post by Decrius
Ah, so with a copy constructor I should reallocate the dynamic memory, and with the assignment operator I would simply pass the pointer so they both point to the same dynamic memory?


Heavens above, no (unless you happen to be writing some kind of very specialised class and know exactly what you are doing).

Consider a daft string class that works the way you describe (although looking at the copy constructor rather than assignment operator - the issue is much the same):


#include <cstring>
using namespace std;

class string
{
private:
char *data;

public:
string(const char *s=""){ data=new char[strlen(s)+1]; strcpy(data,s); }
string(const string &s){ data=s.data; }
~string(){ delete [] data; }

const char *c_str() const { return data; }
};


In a simple test, this appears to work:


void f()
{
string a="hello";
string b=a;

cout << a.c_str() << "\n";
cout << b.c_str() << "\n";
}


HOWEVER - consider what happens here:


void f()
{
string a="hello";

if(some_condition())
{
string b=a;

cout << b.c_str() << "\n";
}

cout << a.c_str() << "\n"; // blammo!
}


Consider what happens when you reach the closing brace of the if block - string b goes out of scope, so is destroyed. So string b's destructor is called. So delete [] is applied to b's data member, releasing the memory it points to.

Since b's data member is pointing at the same memory that a's data member is pointing to, the memory that a's data member is pointing too has also been freed.

However, a has no way of knowing this and still thinks it is a valid string.

Come the next time you access a's data, boom. Maybe it works. Maybe it doesn't. Maybe the monitor explodes.

In this simple example (and this is NOT advocating writing your own string class BTW), you can see a good example of the difference between the copy constructor and the assignment operator:


string::string(const string &s)
{
data=new char[strlen(s.data)+1]; strcpy(data,s.data);
}

const string &operator=(const string &s)
{
delete [] data; data=new char[strlen(s.data)+1]; strcpy(data,s.data); return *this;
}


In the assignment, since we already have a valid string, we have to first release it's data before we can re-construct it as a copy of the parameter.

In the copy constructor we don't have a valid string, so we don't need to.

This difference is subtle with this strawman example, but important.

Share this post


Link to post
Share on other sites
Quote:
Original post by EasilyConfused
string::string(const string &s)
{
data=new char[strlen(s.data)+1]; strcpy(data,s.data);
}

const string &operator=(const string &s)
{
delete [] data; data=new char[strlen(s.data)+1]; strcpy(data,s.data);
}


In the assignment, since we already have a valid string, we have to first release it's data before we can re-construct it as a copy of the parameter.

In the copy constructor we don't have a valid string, so we don't need to.

This difference is subtle with this strawman example, but important.


Yeah thanks :-), makes all sense now xD

Btw, in your first example...you defined a copy constructor, not an assignment operator. The code of the copy constructor is not used... (or did you mean what happens if there is no assignment operator? In that case it's good proof to make one x ;-)).

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!