Question about operator overloading

  • Thread starter yungman
  • Start date
  • Tags
    Operator
In summary, the conversation discusses the use of reference parameters in operator overloading and the importance of understanding the concept. The conversation also touches on the use of copy constructors and their relationship to reference parameters. It is explained that passing by reference allows for the object itself to be modified, rather than a copy of the object. The conversation ends with a clarification that passing by value to the copy constructor is not possible and can result in errors.
  • #1
yungman
5,755
293
In operator overload I often see the parameter is always a reference parameter. I want to find out why. I read the Copy Constructor in the book. I want to verify whether I understand this correctly as it's really important. I use "xx" just to represent some operator like =, >> etc.
C++:
class ABC;
void operator xx (ABC & obj);

This is the copy of the two pages in the book explaining why and I had to read it quite a few times to understand it. I still want to verify here:
CopyC3.jpg
The way I understand it, if I write it like this
C++:
 void ABC xx (ABC obj)
, the operator function will first make a copy of the object of the RIGHT side of the xx and call it obj. But then when obj is first created, it will call the Copy Constructor and create another copy of obj(say called obj2), then when obj2 is created, it will call the Copy Constructor again and create another obj( called obj3) and it will keep doing it over and over until it bombed.

That's the reason it has to pass by reference so NO copy is created and no Copy Constructor will be called at this point.

This is the pdf of the Gaddis book, it's in Page 818 - 819:https://cplusplushelp.weebly.com/uploads/2/5/6/5/25655197/0136022537.pdf

Thanks
 
Technology news on Phys.org
  • #2
yungman said:
In operator overload I often see the parameter is always a reference parameter. I want to find out why. I read the Copy Constructor in the book. I want to verify whether I understand this correctly as it's really important. I use "xx" just to represent some operator like =, >> etc.
C++:
class ABC;
void operator xx (ABC & obj);
No, you're misunderstanding what you posted from the Gaddis book. This is about copy constructors, a type of constructor that creates a new object, not from scratch but copied from an existing object.
This has nothing to do with operators.

In the context of the code you posted, you would have something like this:
C++:
class ABC
{
   <data members>
    public:
       ABC();                              // prototype of default constructor
       ABC(const ABC& obj);  // prototype of copy constructor
      <other function members and operators>
};
 
  • #3
Thanks Mark, I am not asking about Copy Constructor, I am trying to understand why I see a lot of overloading operator function have argument that is reference to the object (Length&right) rather than the object(Length right) in the argument. for example
C++:
 Length L1, L2, L3;  L3 = L1 + L2;
where Length is a class.
The overloading operator is
C++:
 Length operator+(const Length &right)
If instead if I use
C++:
 Length operator+(const Length right)
, compiler will create object right and copy L2 into it, compiler will call Copy Constructor to initialize right. And then the cycle repeats creating a copy of right.

My question has really nothing to do with Copy Constructor, more WHY I have to pass the parameter by reference. Just happens that the book explain this in Copy Constructor.

ThanksEdit:

I read it over, it seems if I don't have a Copy Constructor, then I won't have problem not passing the object as parameter. There will be no Copy Constructor to keep copying the object over and over.
 
Last edited:
  • #4
yungman said:
I am not asking about Copy Constructor, I am trying to understand why I see a lot of overloading operator function have argument that is reference to the object (Length&right) rather than the object(Length right) in the argument.
A reference to an object is actually the closest thing you can get to the object itself. You are assuming that when you pass by value, f(Length right), you'd pass the object. That is not the case. You are actually passing a copy of the object. It is the &-case that passes the object. You can easily see that with code like
Code:
void f(int value) {
 value +=1;
}

void g(int& value) {
 value +=1;
}

int main() {
  i = 1;
  f(i);
 cout<<i<<endl;
 g(i);
 cout<<i<<endl;
}
In the first case, you create a copy of i, increase it, then throw away the copy. So nothing happens (in fact, you might even get at least a compiler warning for that function because it does nothing). In the second case, you pass the variable i itself to the function (or a reference to it, if you think there is a difference between the two), so the object that is modified in g is actually i.

Passing by reference (the object itself) instead of by value (copies) is not exclusive to operator overloading but done all over the place. Obviously, you do that when you want to modify the parameter that was passed (in cases like updateMyObject(MyObject& object)). But usually the reason is just to avoid the copy.

That has indeed not so much to do with the copy constructor. But since it has been mentioned maybe a comment on this: Passing by value to the copy constructor (assuming this was possible for the sake of the argument) is a special case. It is not just inefficient, as for other functions, it is simply not possible. Creating the copy for passing by value requires the copy constructor which requires a copy for passing by value which ... .
 
  • #5
It should also be mentioned that pass by value versus pass by reference is possible in many languages, not just objects in an object oriented language. When the difference is not fully understood, many errors result.
 
  • Like
Likes Timo
  • #6
yungman said:
I am not asking about Copy Constructor, I am trying to understand why I see a lot of overloading operator function have argument that is reference to the object (Length&right) rather than the object(Length right) in the argument.
If you weren't asking about copy constructors, then why did you post a screenshot of a section titled "Copy Constructors and Function Parameters" where you underline two phrases that were directly related to copy constructors?
 
  • #7
The original title was also "Copy Constructors".

The fundamental issue appears to be call by reference vs. call by value as it applies to user-defined objects. And as Timo says, you don't really have a true call by value with objects. Operator overloading happens to be the use case here.
 
  • #8
Thanks everyone, I think the way I started the thread is miss leading. I don't think I expressed myself clearly. I think I understand now. It has to do with copy constructor in the sense that IF there is a copy constructor in the program, any overload operator having argument like (ABC obj) likely will have problem as the operator function create the object obj to copy the object on the right side into obj. This trigger the Copy Constructor to create another Copy of obj and the process repeat over and over. Thereby, it's critical to pass by reference so the function operator won't create a new object and never trigger the Copy Constructor.

The best way is NOT to have a Copy Constructor, then nothing will happen even passing to object.

Yes, this is confusing, I don't think I express myself clearly and I don't think anyone really understand what I ask in this thread that go round and round. Just like in the book. I had to read it like 10 times to get what the book trying to tell me. Read the paragraph of the book in the first post and you'll see. That's the nature of the Copy Constructor that can run into big problem.

Thanks
 
Last edited:
  • #9
yungman said:
Read the paragraph of the book in the first post and you'll see.
It seemed pretty clear to me that Gaddis was talking about copy constructors. If an overloaded operator needs to copy one object's values to another that needs to be created, then, yes, a copy constructor comes into play. But the main point Gaddis was trying to make was why a copy constructor needs a reference parameter, period.

yungman said:
The best way is NOT to have a Copy Constructor, then nothing will happen even passing to object.
No, you've drawn the completely wrong conclusion.
If you have a simple class, then the copy constructor you get for free (the default copy constructor that the compiler generates if you don't write one yourself) will work just fine, but if the class is a bit more complicated, the default copy constructor will produce bad results.

Here's an example to show what I mean.
C++:
#include <iostream>
using std::cout;
using std::endl;

class Foo
{
private:
    float *m_data;
    int m_arrayLen;

public:
    Foo(float x = 0.0, float y = 0.0)
    {
        m_data = new float[2];
        m_data[0] = x;
        m_data[1] = y;
        m_arrayLen = 2;
    }

    // Foo(const Foo& obj) {};  - Let the compiler generate a copy constructor
    ~Foo() // destructor
    {   
        delete[] m_data;
    }    

    float getX()
    {
        return m_data[0];
    }

    float getY()
    {
        return m_data[1];
    }
};

int main()
{
    Foo obj1(2.5, 3.2);
    {                      // obj2 is local to this block
        Foo obj2(obj1);
        cout << "obj2 x value: " << obj2.getX() << endl;
        cout << "obj2 y value: " << obj2.getY() << endl;
    }                    // obj2 is now out of scope, so gets destroyed
    // Unfortunately, obj2 being destroyed affects obj1 adversely
    cout << "obj1 x value: " << obj1.getX() << endl;
    cout << "obj1 y value: " << obj1.getY() << endl;
}
The problem with this code is that the default copy constructor does only a shallow copy, which means that when obj2 is created, it gets the same value for m_data, and the same values for the two array elements.
When obj2 goes out of scope (and gets destroyed), the memory that its m_data member was pointing to gets recycled, so we get garbage the value for obj1.
This is why you sometimes need to write your own copy constructor.
 
  • Like
Likes Vanadium 50
  • #10
I am still looking into your program. I stepped through the program step by step using F11. I observed two things.

1) I don't see program calling copy constructor, maybe it's invisible to the steps.
2) This is more important: when I step pass line 43, it jumped to Lin2 22 which is ~Foo, the Destructor.

I don't know why the program does that, it shouldn't go to destructor until after the last line of the program. The program deleted m_data, that's why you lost the value.

As for copying, the program did copy m_data[0] and m_data[1] from obj1 to obj2. That works.

As for my comment on avoid using copy constructor. I stand by what I said. If I were to do copying, I would use operator=() function, not rely on copy constructor. You have more control with overloading operator=().

I am going to experiment with the program more with a real constructor, not depends on compiler generated one. I read the book on that paragraph so many time, it talked about the Copy Constructor will go into infinite loop if not using &obj in the argument and explain why. That's the point I am working on. I fear that if you don't use &obj in other overload function, a Copy Constructor might send it to infinite loop over writing memory. I need to prove what I read in the book by stepping through the program( don't let it go out of hand) step by step to see what happens.

I don't think we are on the same page in this at all. I'll be back.
 
  • #11
BTW, I forgot to mention that is so important that I put this in separate post. You should NOT even write program like your example. Gaddis spent two pages explaining why Copy Constructor is NOT good for that. The reason is Copy Constructor literally copy everything, you m_data is a POINTER to a memory location, so if you copy literally over, m_data of both obj1 and obj2 will point to the SAME memory. So you modify one, you disrupt the other. That's the reason I said I would avoid using Copy Constructor at all cost.

These are all new, I have to think through things slowly and cannot response right away, takes me time to remember all the little details. For reference, it's in page 816 to 817 in the book that I linked the pdf file.

But still it doesn't explain why your program call the Destructor prematurely. That's a whole different problem
 
  • #12
BTW, I forgot to mention that is so important that I put this in separate post. You should NOT even write program like your example. Gaddis spent two pages explaining why Copy Constructor is NOT good for that. The reason is Copy Constructor literally copy everything, you m_data is a POINTER to a memory location, so if you copy literally over, m_data of both obj1 and obj2 will point to the SAME memory. So you modify one, you disrupt the other. That's the reason I said I would avoid using Copy Constructor at all cost.

These are all new, I have to think through things slowly and cannot response right away, takes me time to remember all the little details. For reference, it's in page 816 to 817 in the book that I linked the pdf file.
 
  • #13
I modified your program and added in the Copy Constructor. Here is the program. I simplified your m_data[] to m[]. I know you don't like it, but I have to type this many times in stepping through debugger. So I down sized it.
C++:
#include <iostream>
using std::cout;
using std::endl;
class Foo
{private:    float *m;    int m_arrayLen;

public:
  Foo(float x = 0.0, float y = 0.0)
  { m = new float[2]; m[0] = x; m[1] = y; m_arrayLen = 2;}
  Foo(const Foo& obj)//Added Copy Constructor from original program.
  {  m = new float[2];
     for (int i = 0; i < 2; i++)
     { m[i] = obj.m[i]; }
  }

  ~Foo() // destructor
    {     delete[] m; } 
    float getX()  {  return m[0]; }
    float getY() { return m[1];}
};

int main()
{  Foo obj1(2.5, 3.2); // obj2 is local to this block
    { Foo obj2 = obj1;
      cout << "obj2 x value: " << obj2.getX() << endl;
      cout << "obj2 y value: " << obj2.getY() << endl;
    }              //Why going to Desstructor from here?
    cout << "obj1 x value: " << obj1.getX() << endl;
    cout << "obj1 y value: " << obj1.getY() << endl;
}
This works and copying over and all displayed correctly.

BUT

What I don't understand is it jump to the Destructor TWICE. First from line 27 and second from line 30. I thought Destructor is called only after the last line (line 31). Why called from line 27?

I was going to delete the & in the argument to make it (const Foo obj) to make it go into the loop, but compile would not allow me to do that. So I cannot proof my point of the paragraph quoted in the book.

Thanks for your time.
 
  • #14
From post #10:
yungman said:
1) I don't see program calling copy constructor, maybe it's invisible to the steps.
Because I didn't include a copy constructor. You would need to look at the assembly code to see what the compiler generated.
yungman said:
2) This is more important: when I step pass line 43, it jumped to Lin2 22 which is ~Foo, the Destructor.

I don't know why the program does that, it shouldn't go to destructor until after the last line of the program. The program deleted m_data, that's why you lost the value.
I explained why this happens, both as comments in the code, and in text following the code. After line 43, obj2 is no longer in scope, so it gets destroyed. That's why the destructor ~Foo() is called.

yungman said:
As for copying, the program did copy m_data[0] and m_data[1] from obj1 to obj2. That works.
But what doesn't work is that it copies the address in obj1.m_data to obj2.m_data. That is the wrong thing to do.
yungman said:
As for my comment on avoid using copy constructor. I stand by what I said. If I were to do copying, I would use operator=() function, not rely on copy constructor. You have more control with overloading operator=().
Nonsense.
operator=() is an overloaded assignment operator, which is different from copying the value of an existing object to one that is being created.
Here's the difference:
C++:
Foo a(2.5, 3.5), b;
b = a;               // Call operator=()
Foo c(a);         // Call copy constructor

The point of the code I posted was to rebut your assertion that it is wise to avoid copy constructors. In my code, I do not include a copy constructor, which is your stated preference, but the program I showed wipes out the values of an existing object when it should not do this.
 
  • #15
Mark44 said:
From post #10:
Because I didn't include a copy constructor. You would need to look at the assembly code to see what the compiler generated.
I explained why this happens, both as comments in the code, and in text following the code. After line 43, obj2 is no longer in scope, so it gets destroyed. That's why the destructor ~Foo() is called.

But what doesn't work is that it copies the address in obj1.m_data to obj2.m_data. That is the wrong thing to do.
Nonsense.
operator=() is an overloaded assignment operator, which is different from copying the value of an existing object to one that is being created.
Here's the difference:
C++:
Foo a(2.5, 3.5), b;
b = a;               // Call operator=()
Foo c(a);         // Call copy constructor

The point of the code I posted was to rebut your assertion that it is wise to avoid copy constructors. In my code, I do not include a copy constructor, which is your stated preference, but the program I showed wipes out the values of an existing object when it should not do this.
I disagree. I rather use = overload. You read the two pages in the book telling you why it's bad to use Copy Constructor? The exact example what you are doing? You read the paragraph in my first post about the Copy constructor can go into a loop without &? I repeat this many times already.

I know you did not put in the Copy Constructor, that's why I put one into make it work. I don't know what the compiler do without it.

I know you comment on it, But I read in the book that Destructor only run once right before the program exit. What trigger the destructor after line 27?
 
  • #16
yungman said:
BTW, I forgot to mention that is so important that I put this in separate post. You should NOT even write program like your example. Gaddis spent two pages explaining why Copy Constructor is NOT good for that. The reason is Copy Constructor literally copy everything, you m_data is a POINTER to a memory location, so if you copy literally over, m_data of both obj1 and obj2 will point to the SAME memory.
I do not believe this is what Gaddis said. It's more likely that he was talking about a default copy constructor, one that is provided by the compiler, and that blindly copies everything over.

If I add this copy constructor to the code I posted, then things work as expected. When obj2 gets destroyed, obj2 still contains the correct values.
C++:
Foo(const Foo& obj) 
{
    m_data = new float[2];
    m_data[0] = obj.m_data[0];
    m_data[1] = obj.m_data[1];
    m_arrayLen = 2;
};
 
  • #17
yungman said:
What I don't understand is it jump to the Destructor TWICE. First from line 27 and second from line 30. I thought Destructor is called only after the last line (line 31). Why called from line 27?
A destructor is called when an object goes out of scope. This happens in line 27, when obj2 goes out of scope, and again after line 30 when obj1 goes out of scope.
yungman said:
I know you did not put in the Copy Constructor, that's why I put one into make it work. I don't know what the compiler do without it.
The copy constructor that the compiler provides is a stupid one -- it does what's called a "shallow copy," which led to the exact problem that I described. Your copy constructor is essentially the same as what I posted in post #16. Your version and my version work as expected, unlike what the default copy constructor does.

yungman said:
I disagree.
On what basis do you disagree? Is it because of your vast and superior knowledge of C++ and object-oriented programming? And this is knowledge you gained in, what, the last month?
I guarantee you that everyone who has replied in your posts has vastly more experience in C++ than you do. It is very arrogant of you to say that you disagree with someone when you don't even understand what it is they're saying.

yungman said:
I rather use = overload. You read the two pages in the book telling you why it's bad to use Copy Constructor? The exact example what you are doing? You read the paragraph in my first post about the Copy constructor can go into a loop without &? I repeat this many times already.
If you are talking about what you posted in post #1, you have completely misinterpreted what Gaddis said. He is not saying that it's bad to use a copy constructor -- he's telling you why the parameter needs to be a reference parameter rather than a value parameter.

And again, operator=() and a copy constructor are different things, used for different purposes. This is sort of like saying, "I rather use overloaded operator<<() instead of cout."
 
  • Love
Likes Vanadium 50
  • #18
Mark44 said:
If you are talking about what you posted in post #1, you have completely misinterpreted what Gaddis said. He is not saying that it's bad to use a copy constructor -- he's telling you why the parameter needs to be a reference parameter rather than a value parameter.

And again, operator=() and a copy constructor are different things, used for different purposes. This is sort of like saying, "I rather use overloaded operator<<() instead of cout."
This is the reason I made this thread, the way the book described that the Copy constructor will go into an endless loop, can happen even on the other overload operators if their argument is (classtype ABC) if you have a copy constructor. Too bad that VS do not allow me to do this, it will flag error if I missed the &.

I did not start out drawing conclusion that Copy Constructor is bad in my original post, more a question about the importance of using reference &. The way the paragraph described, it can extend to other overloading operators that use (classtype ABC) without &. I want to try to understand this and this was my very question.

What is the advantage of using Copy Constructor that operator=() cannot do?

Thanks
 
  • #19
yungman said:
The way the paragraph described, it can extend to other overloading operators that use (classtype ABC) without &.
No. Again, that is the wrong conclusion to reach -- that the three paragraphs can be extended to other than copy constructors. The first paragraph discusses how object parameters get passed to a function.
The second paragraph starts off,
This is why C++ requires the parameter of a copy constructor to be a reference object.
The main thrust of what you posted from Gaddis is about why the object parameter in a copy constructor has to be a reference parameter. It's a mistake to think that this requirement extends to all other functions or operators of a class.

yungman said:
What is the advantage of using Copy Constructor that operator=() cannot do?
It can be very frustrating to give you an answer in post #N, and have you ask the same question in post #(N + k,) with k > 0.

Please (re)read posts #9 and #14 carefully.
 
  • Like
Likes Vanadium 50

FAQ: Question about operator overloading

What is operator overloading?

Operator overloading is a feature in object-oriented programming that allows operators (such as +, -, *, /) to be redefined for user-defined data types. This allows for more intuitive and flexible use of operators with custom objects.

Why is operator overloading useful?

Operator overloading allows for more natural and concise code, as well as making custom objects behave more like built-in types. It can also improve code readability and reduce the amount of code needed for certain operations.

What are the limitations of operator overloading?

One limitation of operator overloading is that it can sometimes lead to confusion or unexpected behavior if not used carefully. It also cannot be used to create new operators, only redefine existing ones. Additionally, not all operators can be overloaded.

How do you overload an operator?

To overload an operator, you must define a function that implements the desired behavior for that operator. This function must be a member function of the class that the operator is being overloaded for, or a global function with at least one parameter of the class type.

Can operator overloading be used in all programming languages?

No, operator overloading is a feature specific to certain programming languages, particularly those that support object-oriented programming. Examples of languages that support operator overloading include C++, Java, and Python.

Similar threads

Replies
89
Views
5K
Replies
23
Views
2K
Replies
52
Views
3K
Replies
31
Views
2K
Replies
36
Views
2K
Replies
4
Views
1K
Replies
1
Views
4K
Replies
17
Views
2K
Replies
3
Views
690
Back
Top