and sign in function return definition

Hello!

I have defined my own class usign template for dealing with vectors. I have been looking a little at other people examples and the and sign got my attention.

I was defining operators to use them with my class and my quetion is what is the difference wheater i use & or not in code bellow:

1
2
3
4
5
6
v3<any_type>& operator = (const v3<any_type>& a){
    x=a.x;
    y=a.y;
    z=a.z;
    return *this;
    }

comparing to:
1
2
3
4
5
6
v3<any_type> operator = (const v3<any_type>& a){
    x=a.x;
    y=a.y;
    z=a.z;
    return *this;
    }



do i save some memory using the & ? Does using it mean we dont make a copy of the variable on left side of operator? And that makes it better to have & there?

Does that mean i should use & in all operator overloadings? +, -, *, ^ ? (* and ^ are dot and cross products)


Thank you for all your help in advance!
Last edited on
what is the difference wheater i use & or not

using & you're passing by reference, without it you're passing by value
http://courses.washington.edu/css342/zander/css332/passby.html

do i save some memory using the &

from the above link: "pass by value means you are making a copy in memory of the actual parameter's value that is passed in ...In pass by reference (also called pass by address), a copy of the address of the actual parameter is stored" ... so yes, you do save some memory when you pass by reference

Does using it mean we dont make a copy of the variable on left side of operator?

first off you're passing arguments to the function and these are on the right side of the operator, variable a in your example

And that makes it better to have & there

yes, wherever feasible because it can avoid expensive copying. but it's not feasible always, see below ...

Does that mean i should use & in all operator overloadings

no, only when it makes sense. suppose you have a vector of integers and you want to sort this vector and print it. if you pass this vector to std::sort() by reference then it will sort the actual vector, not it's copy, and so the original sequence of numbers in this vector will be lost which would be troublesome if you needed it for later use. similarly, if you try to overload the operator + for a class, you'd do something like this:

class Foo{private: int _x;};
Foo Foo::operator + (const foo& f) const
{
Foo sum;
sum._x = _x + f._x;
return sum;
}

Here the variable sum is a local variable, restricted to the scope of the function and is destroyed once the function returns. So you need to return a copy of the variable from the function since the original variable is destroyed, hence passing by reference will not work

Finally, in your copy assignment operator overload you need to check first that the object is not assigned to itself:

v3<any_type>& operator = (const v3<any_type>& a)
{
if (this == &a)//object assigned to itself
return *this;
else
{
x=a.x;
y=a.y;
z=a.z;
return *this;
}
}
gunnerfunner,
I think that saying it has to do with "passing by reference" is misleading. Passing by reference is more fitting in the context of referenced parameters - OP's question concerns the return type of the overloaded operator.

mcf3lmnfs,
Consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>

class Employee {
public :

	Employee();

	Employee& set_salary(const int);
	Employee& set_position_title(const std::string);

	void print_about_me() const;

private :

	int salary;
	std::string position_title;

};

Employee::Employee() : salary(100), position_title("Janitor") {

}

Employee& Employee::set_salary(const int new_salary) {
	this->salary = new_salary;
	return *this;
}

Employee& Employee::set_position_title(const std::string new_position_title) {
	this->position_title = new_position_title;
	return *this;
}

void Employee::print_about_me() const {
	std::cout << "I'm the " << this->position_title << ". My salary is $" << this->salary << "." << std::endl;
}

int main() {

	::Employee employee;

	employee.set_salary(5000).set_position_title("CEO");

	employee.print_about_me();

	std::cin.ignore();
	return 0;
}
I'm the CEO. My salary is $5000.


This is a naive 'Employee' class. By default, each employee is a janitor, and has a salary of $100. In the main function we create an Employee instance called 'employee'. We then chain together both member functions ('set_salary' and 'set_position_title') to modify 'employee'.
This works, because both of these member functions return a reference to the specific Employee instance which invokes them. Now, let's change the code a bit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>

class Employee {
public :

	Employee();

	Employee set_salary(const int);
	Employee set_position_title(const std::string);

	void print_about_me() const;

private :

	int salary;
	std::string position_title;

};

Employee::Employee() : salary(100), position_title("Janitor") {

}

Employee Employee::set_salary(const int new_salary) {
	this->salary = new_salary;
	return *this;
}

Employee Employee::set_position_title(const std::string new_position_title) {
	this->position_title = new_position_title;
	return *this;
}

void Employee::print_about_me() const {
	std::cout << "I'm the " << this->position_title << ". My salary is $" << this->salary << "." << std::endl;
}

int main() {

	::Employee employee;

	employee.set_salary(5000).set_position_title("CEO");

	employee.print_about_me();

	std::cin.ignore();
	return 0;
}
I'm the Janitor. My salary is $5000.


All I've changed in this snippet are lines 9, 10, 25 and 30 - I've removed the ampersands ('&'). The member functions no longer return a reference to the invoking object, but a copy of it. As you can see, the output is not what we expect. The salary has changed from $100 to $5000, but the position title hasn't changed from "Janitor" to "CEO". This is because the chained call to 'set_salary' affects our 'employee' object, but the chained call to 'set_position_title' is only modifying an anonymous copy returned by 'set_salary', which ceases to exist immediately.

In short, returning by reference can be very useful to chain methods together like this.
Last edited on
OP's question concerns the return type of the overloaded operator.

if you'd read carefully you'd have found that covered as well
Thank you all for your comments! Now i have some more detailed question about it.
So, about this code:
1
2
3
4
5
6
v3<any_type>& operator = (const v3<any_type>& a){ 
     x=a.x;
     y=a.y;
     z=a.z;
     return *this;
     }


Now as you mentioned passing argouments by reference. a is indeed passed by reference and changing a.x or any a's element would actually change the incoming paramet on outer scope yes?

Continuing assuming that true, now about x, y and z, This is a part i get confused by. Because x, y and, there is even no part where to define if is passed by reference or not. It just sits there, i assume because this is a function that is called from the variable that is of type v3 class, that sits on the left side of assigment operator =. And because it is called from here, we already have these x,y,z declared and initialized. No need to copy or use its reference. is this correct?

Now there is an & at tge begining of the code. That one should define the return type of my function. I have tried using it, and i have tried without it.
I get the same result in both times. Both correct, (looking at numbers that should be a result of calculation). Now this is my actual question indeed. What exactly does that & then change? Does it have any effect on memmory efficiency? does it have any effect on performance of the code? I actually see no point in that &. I see a point where xismn ussed it. But in my case does it do anything? Why does it work in both ways? which is prefered, better, why?


Last edited on
changing a.x or any a's element would actually change the incoming paramet on outer scope yes...

By 'outer scope' I suppose you're referring to *this object? In that case the answer is yes but it has nothing to do with passing by reference but rather due to the assignment statements x = a.x, etc

Because x, y and, there is even no part where to define if is passed by reference or not.

Not correct; x, y and z are part of a which is passed by reference and that determines how these variables are passed

I get the same result in both times.

In this case you would but try overloading the + operator as I showed in my previous post and returning by reference, your compiler will start complaining and the reason for this is as alluded to in that post. Read that bit carefully because it is critical in choosing the type of return a function should have

which is prefered, better, why?

From my previous post why passing (and returning) by reference is preferable, wherever it makes sense to do so:
yes, wherever feasible because it can avoid expensive copying ...

I suggest you re-read that post carefully and the link at the top from washington.edu - the material under the link is quite comprehensive
Now as you mentioned passing argouments by reference. a is indeed passed by reference and changing a.x or any a's element would actually change the incoming paramet on outer scope yes?
If that were allowed, yes. However, the parameter is declared as const, which means the compiler will reject any attempt to modify that parameter.

An important reason for the & in the return type of the function is to allow the chaining of operations.
I came up with a rather contrived example - I'm sure there are better ones.
Here there is a user-defined type Number which aims to behave in a very similar manner to the built-in numeric types - though this code is not thorough and complete.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>

class Number {
    double value;
public:
    Number() : value(0.0) { }
    Number(double n) : value(n) { } 
    
    Number & operator+=(const Number & n)
    {
        value += n.value;
        return *this;
    }
        
    Number & operator*=(const Number & n)
    {
        value *= n.value;
        return *this;
    }
    
    friend std::ostream & operator << (std::ostream & os, const Number & n)
    {
        return os << n.value;
    }
}; 

int main()
{
    {
        double a = 2; 
        double b = 3;        
        double c = 5;
        double d = 7;
        ((d += b) *= a) += c;
        std::cout  << "type double:\n" << "  d = " << d << '\n';
    }

    {
        Number a = 2; 
        Number b = 3;        
        Number c = 5;
        Number d = 7;
        ((d += b) *= a) += c;
        std::cout  << "type Number:\n" << "  d = " << d << '\n';
    }
}

The rather unsightly-looking expression at lines 34 and 43 chains a series of operations, one after the other.
 
((d += b) *= a) += c;

1. d += b the result of 7 +3 is calculated, and assigned to d
2. *= a the current value of d (10) is multiplied by 2 and the result stored in d.
3. += c the current value of d (20) is added to 5 and the result stored in d.
Final result d = 25.

However, if either or both of the & are omitted in the function definitions at lines 9 and 15, instead of the later operations modifying d, they instead operate on a temporary variable instead, giving a different result.

Sorry if this example a bit confusing.
Topic archived. No new replies allowed.