4

Im writing a copy assignment operator for a class I've created and I'm using a previous post as a guide: What is The Rule of Three?

Im a bit confused on one aspect of this person's explanation.

Here is their class:

class person
{
    char* name;
    int age;
};

Here is the copy assignment operator definition that I am using as a reference (there are at least 2 offered):

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

What I'm finding confusing is, why are they including the line delete[] name;?

Here is the other example they provide:

person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

I immediately shied away from this one because I couldn't understand why the function checks if(this != &that) and then runs delete (delete[] name;) on an array that doesn't seem to have been generated yet. When the assignment operator is invoked, is the regular constructor called immediately before the copy assignment operator function is called? Therefore meaning that we must delete the array that was generated by the classes constructor because it can only be full of junk data?

Why can't I just write: name = that.name or

name = new char[that.name.size()];
for (int i = 0; i < that.name.size(); i++)`
{
  name[i] = that.name[i]
}

This is probably really basic and I should just implement what the post suggests but my actual use-case involves an array of structs with multiple members and so Im just having a little bit of a difficulty understanding what exactly I need to do.

I realize there are like 2.5 questions here. Any insight would be appreciated.

This is my first time implementing custom copy constructors and copy assignment operators and Im sure it will seem easy after I've done it a few times.

Thanks in advance.

8
  • 1
    If the old and new namewould be of the same length you could reuse the name pointer. But if not, it must point to a new place. Commented Dec 16, 2019 at 21:04
  • person temp(that); std::swap(temp.name, name); std::swap(temp.age, age); return *this; Using copy/swap instead of what you have in your question, that would be the entire assignment operator, providing that there is a copy constructor and destructor. Commented Dec 16, 2019 at 21:06
  • More on Copy and Swap. Commented Dec 16, 2019 at 21:20
  • Can copy() and swap() be used on arrays of structs? Commented Dec 16, 2019 at 21:22
  • 1
    operator= runs on objects that already exist. The object must have been constructed at some point earlier in the code so that you can do assignment on it. E.g. Person p; /* ... */ p = q; Commented Dec 16, 2019 at 21:52

3 Answers 3

9

All of this is needed to make sure that memory is managed correctly.

If I understand correctly, you need an answer to when is operator= actually called?
Well, operator= is always called when two valid objects exist. One of them is being assigned to, and second one is source of data. After this operation, both object must still remain valid.

This means than inside operator= you have this object with memory allocated for a string (which was allocated in one of the contrusctors) and that object, also with memory allocated for another string.

What I'm finding confusing is, why are they including the line delete[] name;?

We have to first clean up memory which is currently residing in this object, or else we would lose this pointer after assigning new momry to it.

When the assignment operator is invoked, is the regular constructor called immediately before the copy assignment operator function is called?

No. The object already exists, that's why operator= is called (and not a copy constructor).

Therefore meaning that we must delete the array that was generated by the classes constructor because it can only be full of junk data?

It is full of valid data. Your object was contructed, it has some data in it, and then you assign something else to this object.


Addendum: When is copy contructor called and when is operator= called? (see this question for more detailed information):

class A{};

int main()
{
    A a1; //default constructor 
    A a2 = a1; //copy-constructor, not assignment operator! New object is needed

    A a3; 
    a3 = a1; //we have valid and existing object on the left side, assignment operator is used
}
Sign up to request clarification or add additional context in comments.

1 Comment

thank you very much, I had already accepted the first answer before I saw this. Both are quite good. Thank you for your time writing this. I will review the linked post as well.
4

What I'm finding confusing is, why are they including the line delete[] name;?

When using new and new[] to manage memory, the rule is thumb is that they should have a matching delete or delete[]. Presumably, name was previously allocated with new[] (either in the constructor or by a previous assignment), so before reassigning it on the next line with name = local_name;, we need to delete[] it.

I couldn't understand why the function checks if(this != &that)

This check is a sanity check. If you assign an object to itself, then there is no need to do anything. In fact, it could cause problems because if you delete[] name; when this points to the same object as that references, then you can no longer copy that.name because its memory has been freed.

Why can't I just write: name = that.name or

This will make name in two different instances point to the same memory. In some cases, such shared memory is not only useful but desired. However, you have to be careful because if one of the instances executes delete[] on its name then name in the other instance is no longer valid.

8 Comments

name was previously allocated with new[] in that object's constructor? That is what is confusing me, why isn't the line delete[] that.name ?. I could understand if we were deleting the old object's memory resources but why are we deleting the new object's memory resources, which I didn't think were even allocated yet? Unless, the object's constructor was run before the copy assignment operator code is run.
@MagicGAT "name was previously allocated with new[] in that object's constructor?" Yes, either in the constructor or in a previous call to operator=().
@MagicGAT "That is what is confusing me, why isn't the line delete[] that.name ?. I could understand if we were deleting the old object's memory resources but why are we deleting the new object's memory resources" Don't think of these as the "old" vs "new" objects. They are the left and right side of the assignment operators. The object on the left presumably already has a name field that was allocated memory. But we are overwriting that memory and before we do, we need to free the memory that name points to in order to avoid a memory leak.
ok, that makes more sense. because if are using = on an object, it had to have been instantiated already (even if the C++ code instantiated the object and then assigned it the value of another object in the same line)
@MagicGAT Suppose you have the assignment a = b. Then if operator=() does delete[] that.name;, then b.name no longer points to a valid memory address. This will lead to surprising behavior because we don't expect the assignment to modify b.
|
3

Let's start from the question about this code snippet.

name = new char[that.name.size()];
for (int i = 0; i < that.name.size(); i++)`
{
  name[i] = that.name[i]
}

The data member name has the type char *. That is it is a pointer. Neither pointers nor characters pointed to by pointers have member functions like size.

So this code snippet is invalid and will not compile.

You could use such an approach like this

name = that.name;

if the data member had the type std::string.

Why can't I just write: name = that.name

In this case two objects of the class will contain pointers that point to the same memory extent. So if one object will be deleted then the pointer of other object will be invalid and using it to delete the allocated memory results in undefined behavior.

When the assignment operator is invoked, is the regular constructor called immediately before the copy assignment operator function is called?

The copy assignment operator may be applied only for already constructed object. That is an object of the class must already to exist and the used constructor shall initialize its data member name either by nullptr or by the address of allocated memory.

I immediately shied away from this one because I couldn't understand why the function checks if(this != &that)

This check allows to avoid self-assignment of an object. That is in the case when an object is assigned to itself the code of allocating and deleting memory will be redundant.

What I'm finding confusing is, why are they including the line delete[] name;?

Because the object was already constructed and its data member name can point to allocated memory that contains a string for name.

Taking all what was said into account the copy assignment operator can look like

person& operator =( const person &that )
{
    if ( this != &that )
    {
        char *local_name = new char[strlen(that.name) + 1];
        // If the above statement throws,
        // the object is still in the same state as before.
        // None of the following statements will throw an exception :)
        strcpy(local_name, that.name);
        delete[] name;
        name = local_name;
        age = that.age;
    }

    return *this;
}

1 Comment

Ok, I see now that the copy assignment operator has nothing to do with instantiation. Thank you for this clarification.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.