2

I made the following operator overloading test:

#include <iostream>
#include <string>

using namespace std;

class TestClass
{
    string ClassName;

    public:

    TestClass(string Name)
    {
        ClassName = Name;
        cout << ClassName << " constructed." << endl;
    }

    ~TestClass()
    {
        cout << ClassName << " destructed." << endl;
    }

    void operator=(TestClass Other)
    {
        cout << ClassName << " in operator=" << endl;
        cout << "The address of the other class is " << &Other << "." << endl;
    }
};

int main()
{
    TestClass FirstInstance("FirstInstance");
    TestClass SecondInstance("SecondInstance");

    FirstInstance = SecondInstance;
    SecondInstance = FirstInstance;

    return 0;
}

The assignment operator behaves as-expected, outputting the address of the other instance.

Now, how would I actually assign something from the other instance? For example, something like this:

void operator=(TestClass Other)
{
    ClassName = Other.ClassName;
}
Maxpm
  • 24,113
  • 33
  • 111
  • 170
  • You don't need either, but it still looks odd that you have an assignment operator and a destructor, but no copy constructor. As per the Rule of Three, if you need either, you'll likely need all three. – sbi Dec 22 '10 at 14:54
  • @sbi Of course. This is just some test code, though. – Maxpm Dec 22 '10 at 15:24
  • Still, reflexes kick in when I see that. I also noted that you pass a `std::string` object per copy instead of `const` reference. You might want to read [this](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254). – sbi Dec 22 '10 at 15:32

5 Answers5

5

The traditional canonical form of the assignment operator looks like this:

TestClass& operator=(const TestClass& Other);

(you don't want to invoke the copy constructor for assignment, too) and it returns a reference to *this.

A naive implementation would assign each data member individually:

TestClass& operator=(const TestClass& Other)
{
  ClassName = Other.ClassName;
  return *this;
}

(Note that this is exactly what the compiler-generated assignment operator would do, so it's pretty useless to overload it. I take it that this is for exercising, though.)

A better approach would be to employ the Copy-And-Swap idiom. (If you find GMan's answer too overwhelming, try mine, which is less exhaustive. :)) Note that C&S employs the copy constructor and destructor to do assignment and therefore requires the object to be passed per copy, as you had in your question:

TestClass& operator=(TestClass Other)
Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • I know you know about copy-and-swap, why did you declare the parameter as a reference? – Ben Voigt Dec 22 '10 at 13:51
  • @Ben: Thanks. I've added a note that, using c&s, the object should be copied. Old habits die hard, I guess. (Oh, and I'm not sure what's a "ninja edit", BTW.) – sbi Dec 22 '10 at 13:56
  • In this case, it was a `ninja edit` because you made the changes Ben was suggesting as he was suggesting them. – Bill Dec 22 '10 at 18:00
5

The code you've shown would do it. No one would consider it to be a particularly good implementation, though.

This conforms to what is expected of an assignment operator:

TestClass& operator=(TestClass other)
{
    using std::swap;
    swap(ClassName, other.ClassName);
    // repeat for other member variables;
    return *this;
}

BTW, you talk about "other class", but you have only one class, and multiple instances of that class.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Ben, actually it would be better to provide a `swap()` member function and call that. Nevertheless, this is better than assigning. – sbi Dec 22 '10 at 14:21
3

almost all said, a few notes:

  • check for self-assignment, i.e. if (&other != this) // assign
  • look here for an excellent guide on operator overloading
davka
  • 13,974
  • 11
  • 61
  • 86
  • 1
    If your assignment operator needs a check for self-assignment, chances are there's a better implementation. Good implementations (like Copy-And-Swap) don't need that test (which puts the burden of checking for the rare case on every assignment). – sbi Dec 22 '10 at 14:06
  • 2
    `` We also have an operator overloading FAQ here on SO now: http://stackoverflow.com/questions/4421706/operator-overloading. `` – sbi Dec 22 '10 at 14:06
  • @sbi: thanks for the ref, I'll read it one day ;). The one I mention is short and easy for beginners, giving just bare essentials. I'll also read up the C&S one day, but as for self-test overhead - seems that C&S has an overhead of copying and in many cases memory allocation (if your class contains strings, vectors etc.), so it should have a "handle with care" label, isn't it? – davka Dec 22 '10 at 14:21
  • 1
    @davka: The one you linked to is questionable, though. Also, C&S has no overhead. [I have explained why it doesn't.](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom/3279616#3279616). In short: assignment is tearing down old state, and building up new state by copying data from another object. That's exactly what copy-constructor and destructor do, and C&S manages to employ them in the right order to be exception-safe. – sbi Dec 22 '10 at 14:26
  • @sbi: self-assignment is the exception of no overhead copy-and-swap. But one shouldn't optimize for the rare case anyway. – Ben Voigt Dec 22 '10 at 15:38
  • @sbi: does `swap()` swaps the actual pointers? if not, I don't see how it does not have additional overhead. Suppose you're assigning 2 strings of equal length. In the "traditional" approach you just copy the memory. In C&S, you allocate new memory, copy and deallocate. Am I missing something? – davka Dec 22 '10 at 15:55
  • 1
    @davka: When swapping, you allocate for the new data, copy the new data, _swap_ old and new data, and deallocate the old data. When assigning, you deallocate the old data, allocate for the new data, and copy the data (and you pray allocation won't fail and catch you with your pants down). But swapping is supposed to be O(1) and non-throwing, so it doesn't factor into the runtime. (For example, with `std::vector` swapping will swap two pointers. Comparing to the O(N) of copying and the O(VeryLooong) of allocation, this is neglectable.) – sbi Dec 22 '10 at 16:30
1

Traditionnaly the assignment operator and the copy constructor are defined passing a const reference, and not with a copy by value mechanism.

class TestClass 
{
public:
    //... 
    TestClass& operator=(const TestClass& Other)
    {
        m_ClassName= Other.m_ClassName;
        return *this;
    }
private:
    std::string m_ClassName;
 }

EDIT: I corrected because I had put code that didnt return the TestClass& (c.f. @sbi 's answer)

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • 1
    The new common practice actually does pass the RHS by value. It's called the [copy-and-swap idiom](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). – Ben Voigt Dec 22 '10 at 13:50
  • And just an instictive automatic repulsion about RHS by value... ( without having looked a single second at the thourough SO subject about copy and swap idiom)... RHS by value while using polymorphism has meant such a hundred of bugs in my career... it will take me **hours** to be convinced to using RHS by value ;-) – Stephane Rolland Dec 22 '10 at 14:12
0

You are correct about how to copy the contents from the other class. Simple objects can just be assigned using operator=.

However, be wary of cases where TestClass contains pointer members -- if you just assign the pointer using operator=, then both objects will have pointers pointing to the same memory, which may not be what you want. You may instead need to make sure you allocate some new memory and copy the pointed-to data into it so both objects have their own copy of the data. Remember you also need to properly deallocate the memory already pointed to by the assigned-to object before allocating a new block for the copied data.

By the way, you should probably declare your operator= like this:

TestClass & operator=(const TestClass & Other)
{
    ClassName = Other.ClassName;
    return *this;
}

This is the general convention used when overloading operator=. The return statement allows chaining of assignments (like a = b = c) and passing the parameter by const reference avoids copying Other on its way into the function call.

Nick Meyer
  • 39,212
  • 14
  • 67
  • 75
  • 1
    The new common practice actually does pass the RHS by value. It's called the [copy-and-swap idiom](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). – Ben Voigt Dec 22 '10 at 13:51