Hello, my understanding is 'this' is a pointer to the current class you are working in.
Is this right?

For example in my textbook it has

class ThreeD
{
    int x,y,z;
    
    public:
    ThreeD() {x=y=z=0;}
    ThreeD(int i, int j, int k) {x=i; this ->y =j; z=k;}
    
    ThreeD operator+(ThreeD op2);
    ThreeD operator=(ThreeD op2);
    
    void show();
    
}

ThreeD::ThreeD operator=(ThreeD op2);
{
    x=op2.x;
    y=op2.y;
    z=op2.z; 
    
    return *this;
}

I am a bit confused about the return of (*this).
Is it because if you just returned a void the values wouldn't be changed outside this scope.

Basically I am just not 100% confident that I have grasped whats happening here and want to check there isn't something important I'm missing. I can't find a suitable explanation from google. Thanks :).

Recommended Answers

All 14 Replies

I dont find any reason on why return *this is present there.
If it had to be there it should have been like this.

class ThreeD
{
    int x,y,z;
    
    public:
    ThreeD() {x=y=z=0;}
    ThreeD(int i, int j, int k) {x=i; this ->y =j; z=k;}
    
    ThreeD operator+(ThreeD op2);
    ThreeD& operator=(ThreeD op2);//It should be returning a ThreeD not void
    
    void show();
    
}

It might be like this

&ThreeD ThreeD::operator=(ThreeD op2);//Retuning a ThreeD
{
    x=op2.x;
    y=op2.y;
    z=op2.z; 
    
    return *this;
}

Some of this code is wrong, first of all, the class is missing a semicolon at the end, and you are returning a reference wrong. This is how it should be.

class ThreeD
{
    int x, y, z;
    
    public:
    ThreeD() { x = y = z = 0; }
    ThreeD(int i, int j, int k) { x = i; this->y = j; z = k; }
    
    ThreeD operator +(ThreeD op2);
    ThreeD& operator =(ThreeD op2);
    
    void show();
    
}[B];[/B]

[B]ThreeD &[/B]ThreeD::operator =(ThreeD op2)
{
    x = op2.x;
    y = op2.y;
    z = op2.z; 
    
    return *this;
}

Thanks for your help.

The missing semi-colon was my fault, the online textbook definitely doesn't have an & in it though.

Both ways compile so as long as this isn't just lucky I will just learn without the & sign as it makes more sense to me (though maybe it shouldn't lol).

Since it is a dereferenced pointer it isn't returning an address but an actual ThreeD class. Thats my newb take on it.

Thanks again.

Both ways compile so as long as this isn't just lucky I will just learn without the & sign as it makes more sense to me (though maybe it shouldn't lol).

Without the ampersand (&) the function creates and returns a temporary copy of the object it is acting on. This involves invoking a copy constructor and (when the caller has finished with the returned object) destroying it.

While a compiler is allowed to eliminate temporary objects in some circumstances, it is usually better to return a reference for performance reasons.

Since it is a dereferenced pointer it isn't returning an address but an actual ThreeD class. Thats my newb take on it.

Your "newb take" is wrong.

A dereferenced pointer technically yields a reference. The object that reference refers to can be copied, which is why your operator=() can be implemented as returning an object by value (without ampersand) rather than a reference.

Also, returning an object by reference does not return "an actual ThreeD class". It returns "an instance of ThreeD" or "an object of class ThreeD".

Functions can return objects. They do not return classes.

In addition, operator= sceleton should be:

ThreeD& operator=(const ThreeD& op2)
{
    if (this != &op2)
    {
         ....
    }
    return *this;
}


While a compiler is allowed to eliminate temporary objects in some circumstances, it is usually better to return a reference for performance reasons.

Your "newb take" is wrong.

Thanks, looks like I need to do some more reading then.

You will understand all this if you read just a few tutorials.

Thanks will give those a look over when I get some time, I have read one of them before but forgotten it all now :)

In addition, operator= sceleton should be:

ThreeD& operator=(const ThreeD& op2){ if (this != &op2) { .... } return *this;}

This is checking you haven't done a self-assignment right? Sorry what is sceleton slang for though?


Thanks for all your help everybody.

Ok, I'm slightly confused about what just happened in this topic...

A fair amount of individuals stated that returning a reference of the actual object that represents the class is more preferable than returning a copy that has the reflected changes made on the object.

Sure, for performance reasons, it might be better. But now you risk changing the original object, which may or may not be in respect to the way the class is defined.

Also, since the original object has no pointer-members, the default copy-constructor is suitable for making a copy of the object after the changes are made.

Without the ampersand (&) the function creates and returns a temporary copy of the object it is acting on. This involves invoking a copy constructor and (when the caller has finished with the returned object) destroying it.

While a compiler is allowed to eliminate temporary objects in some circumstances, it is usually better to return a reference for performance reasons.

I don't know about you, but whenever I need to use something (like a copy for example) I only use it in the same scope it is declared/defined.

A not-so-perfect example of copy vs reference--

#include <iostream>

class ThreeD
{
    int x,y,z;

    public:
    ThreeD() {x=y=z=0;}
    ThreeD(int i, int j, int k){setLocation(i, j, k);}
    void setLocation(int i, int j, int k);
    ThreeD operator+(ThreeD op2);
    ThreeD& operator=(ThreeD op2);
    ThreeD copy();
    void show();

};

ThreeD& ThreeD::operator=(ThreeD op2)
{
    x=op2.x;
    y=op2.y;
    z=op2.z;

    return *this;
}

void ThreeD::show(){
    std::cout << "< " << x << ", " << y << ", " << z << " >\n";
}

ThreeD ThreeD::copy(){
    return *this;
}

void ThreeD::setLocation(int i, int j, int k){
    this->x = i;
    this->y = j;
    this->z = k;
}

int main(){

    ThreeD first (3, 4, 5);
    ThreeD *ptr = &(first = first);
    ptr->setLocation(6, 7, 8);
    ptr->show();
    first.show();

    std::cout << "\n";
    ThreeD second (3, 4, 5);
    ptr = &second.copy();
    ptr->setLocation(6, 7, 8);
    ptr->show();
    second.show();

    return 0;
}

-- again, the way you return the object will depend on the intent of the class, though I can understand why many people would first turn to ThreeD& over ThreeD since most = operators are, initially, meant to return the reference of the object.

>This is checking you haven't done a self-assignment right?
Yes, though that check is actually less effective than simply writing your code in such a way as to make it unnecessary.

>Sorry what is sceleton slang for though?
It's slang for skeleton, and I'm sure any dictionary will tell you the definitions.

>But now you risk changing the original object, which may
>or may not be in respect to the way the class is defined.
As far as I can tell, the thread has devolved into a debate about the assignment operator's return value. In the case of the assignment operator, it's generally best practice to match the behavior of built-in types. If you don't return a reference, you fail in such cases as ( a = b ) = c , which is perfectly legal and where the result should have a == c rather than a == b . Here's that exact test with an assignment operator that returns the result by value:

#include <iostream>

class integer {
  int x;
public:
  integer ( int init ): x ( init ) {}

  integer operator= ( const integer& rhs )
  {
    x = rhs.x;
    return *this;
  }

  friend std::ostream& operator<< ( 
    std::ostream& out, const integer& i )
  {
    return out<< i.x;
  }
};

int main()
{
  int a = 1;
  int b = 2;

  ( a = b ) = 123;

  std::cout<< a <<'\t'<< b <<'\n';

  integer c = 1;
  integer d = 2;

  ( c = d ) = 123;

  std::cout<< c <<'\t'<< d <<'\n';
}

Breaking from convention with built-in types should be done with great care. Also consider that your custom assignment operator should match the behavior of the generated assignment operator in how it accepts parameters and returns values. If you remove the explicit assignment operator and do the test again, it runs the same as it would if you returned a reference:

#include <iostream>

class integer {
  int x;
public:
  integer ( int init ): x ( init ) {}

  friend std::ostream& operator<< ( 
    std::ostream& out, const integer& i )
  {
    return out<< i.x;
  }
};

There, two good reasons why the assignment operator should return a reference.

>I don't know about you, but whenever I need to use something (like a copy
>for example) I only use it in the same scope it is declared/defined.
I'm not sure I see how your reply is related to the quoted statement.

>This is checking you haven't done a self-assignment right?
Yes, though that check is actually less effective than simply writing your code in such a way as to make it unnecessary.

>Sorry what is sceleton slang for though?
It's slang for skeleton, and I'm sure any dictionary will tell you the definitions.

>But now you risk changing the original object, which may
>or may not be in respect to the way the class is defined.
As far as I can tell, the thread has devolved into a debate about the assignment operator's return value. In the case of the assignment operator, it's generally best practice to match the behavior of built-in types. If you don't return a reference, you fail in such cases as ( a = b ) = c , which is perfectly legal and where the result should have a == c rather than a == b . Here's that exact test with an assignment operator that returns the result by value:

#include <iostream>

class integer {
  int x;
public:
  integer ( int init ): x ( init ) {}

  integer operator= ( const integer& rhs )
  {
    x = rhs.x;
    return *this;
  }

  friend std::ostream& operator<< ( 
    std::ostream& out, const integer& i )
  {
    return out<< i.x;
  }
};

int main()
{
  int a = 1;
  int b = 2;

  ( a = b ) = 123;

  std::cout<< a <<'\t'<< b <<'\n';

  integer c = 1;
  integer d = 2;

  ( c = d ) = 123;

  std::cout<< c <<'\t'<< d <<'\n';
}

Breaking from convention with built-in types should be done with great care. Also consider that your custom assignment operator should match the behavior of the generated assignment operator in how it accepts parameters and returns values. If you remove the explicit assignment operator and do the test again, it runs the same as it would if you returned a reference:

#include <iostream>

class integer {
  int x;
public:
  integer ( int init ): x ( init ) {}

  friend std::ostream& operator<< ( 
    std::ostream& out, const integer& i )
  {
    return out<< i.x;
  }
};

There, two good reasons why the assignment operator should return a reference.

>I don't know about you, but whenever I need to use something (like a copy
>for example) I only use it in the same scope it is declared/defined.
I'm not sure I see how your reply is related to the quoted statement.

@Quote3: Yes, I thought about the assignments while taking a shower. You're correct @_@

@Quote4: I couldn't confirm if the compiler could optimize variables/objects out of scope or not if it was a copy, or temporary type. I most likely misunderstood the poster with the reply I made.

In addition, operator= sceleton should be:

ThreeD& operator=(const ThreeD& op2)
{
    if (this != &op2)
    {
         ....
    }
    return *this;
}

The above is common practice, but it is easily broken. Imagine, for example, a class that supports its own operator&() - it will not perform as you intend.

Generally, it is better to code in a manner that prevents self-assignment in the first place.

The most useful way I've found to do an assignment operator is;

void ThreeD::swap(ThreeD &op1, ThreeD &op2) 
{
     // do the swap in a way that is guaranteed not to throw exceptions
}

ThreeD& operator=(const ThreeD& op2)
{
    ThreeD temp(op2);
    swap(*this, temp);
    return *this;
}

This approach is exception safe (if an exception is thrown - a normal way of reporting a terminal error) the objects being acted on have no effect. No need to check for self-assignment: the only cost is associated with creating a temporary copy of *this.

This approach is recommended - as one of many practices to be used collectively - in some coding standards designed for using C++ in high criticality systems ie systems that will cause major real-world damage (eg people dying) if they behave incorrectly.

A fair amount of individuals stated that returning a reference of the actual object that represents the class is more preferable than returning a copy that has the reflected changes made on the object.

Yes it is.

Sure, for performance reasons, it might be better. But now you risk changing the original object, which may or may not be in respect to the way the class is defined.

Performance is just one of the advantages. The most usual reason - which many fail to understand - is that an operator=() that returns a copy rather than a reference breaks some code, or make it do things that a programmer does not expect when they see the code.

It is extremely helpful to do things in ways that programmers will reasonably expect - ie consistent with the way other parts of the language are used. This is one of the tenets of programming for high criticality (eg safety critical) systems that code needs to be structured in ways that avoid confusing programmers. Confusing a programmer is one of the best way to develop software with flaws that are difficult to detect. The "behave like basic types" rule that Narue described is a consequence of that - violating it tends to result in programmer confusion.

This is particularly true in team environments. Consider a circumstance where programmer A develops code, programmer B is required to maintain code but programmer A has done little hacks that make code behave in a manner different from programmer B would expect. Programmer B modifies that code, but the changes don't work because of those little differences. Unfortunately, an operator=() returning non-reference is one of those "little hacks". Incidentally the "(this!= &op2) test for avoiding self-assignment is another.

I don't know about you, but whenever I need to use something (like a copy for example) I only use it in the same scope it is declared/defined.

Really??? So you never pass data to functions? You never return data from functions? You never pass a pointer (or reference) to a function? You never do any I/O?

Passing objects (or data) between scopes is a fundamental way most programmers work. Even the simple thing like

fscanf(file_pointer, "%d", &an_integer);

means that file_pointer and an_integer are manipulated by code outside the scope where they are declared/defined.

-- again, the way you return the object will depend on the intent of the class, though I can understand why many people would first turn to ThreeD& over ThreeD since most = operators are, initially, meant to return the reference of the object.

Big of you. While it's true that the method of returning from a function depends on the function (or the class it's a member of) it's a big reach to advocate that operator=() returns a value rather than a reference.

There are many practical reasons to prefer that operator=() returns a reference over return by value. There are some circumstances to prefer returning by value, but those are relatively rare in practice.

It is not exactly a coincidence that authoritative authors (Stroustrup, Meyers, Stepanov) implement assignment operators returning reference without further ado.

commented: ! +9

>> The above is common practice, but it is easily broken.
>> Imagine, for example, a class that supports its own operator&() ...
>> The most useful way I've found to do an assignment operator is;
>> ...
>> the only cost is associated with creating a temporary copy of *this.

we can get the benefits of the recommended approach without incurring the overhead of creating and destroying a temporary copy

#include <boost/utility.hpp>

ThreeD& ThreeD::operator= ( const ThreeD& that )
{
  if ( this != boost::addressof(that) )
  {
    // assign in an exception-safe manner
  }
  return *this;
}

http://www.boost.org/doc/libs/1_36_0/libs/utility/utility.htm#addressof

You miss the point, which is that it is better to avoid the self-assignment test in the first place. I mentioned the "create a temporary and swap" approach as an example of an approach to avoid doing a self-assignment, not as the only way. Apart from classes with an operator&(), the self-assignment test does not work well in class hierarchies either, even with use of boost::addressof().

You will also find that it somewhat difficult to implement an exception-safe assignment operator without creating a temporary copy - of either the object itself, or (at a minimum) of the data it contains. Doing a self-assignment test - even if it works - does not change that.

> the self-assignment test does not work well in class hierarchies either,
> even with use of boost::addressof().

it is unlikely that using value semantics on object-oriented types is something that people would want to do. however, the self-assignment test would work perfectly as long as the overloaded assignment operator is ThreeD& ThreeD::operator= ( const ThreeD& that ) ; that would be a reference to the anonymous base class sub-object (of type ThreeD) and the boost::addressof() would return the address of the ThreeD sub-object.

the 'make temporary copy and swap' technique does not work at all in class hierarchies when the base class ThreeD is abstract.

> difficult to implement an exception-safe assignment operator without creating a
> temporary copy - of either the object itself, or (at a minimum) of the data it contains.
> Doing a self-assignment test - even if it works - does not change that.

not necessarily if the class is designed with exception-safety in mind. for example,

struct A
{
  public:
     // ...
  private: std::list< std::string > strlist ;
};

exception-safe assignment operator can be written without creating a temporary copy of the strings.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.