incomplete / useless method?

Pages: 12
Forgot about the thread, sorry...

I am still confused.
To the question about why I think I am returning a pointer:

return *this
I associate this with a pointer due to the '*'.
If it was a reference, it would have a '&' in the return statement somewhere, wouldn't it?

you need to unlearn what you have learned.

int * ip = new int; //ip is a pointer, and yes the * means pointer HERE.
..
*ip = 13; // *ip is an integer. it is the integer that ip points to. the * does not mean pointer here. Here, it means 'what the pointer points to' or 'dereference' of the pointer.
ip = 13; //bad idea: ip is a pointer, and this changes it to a value that is not likely to be usable. ip is not the integer, its the pointer... note the lack of the *

so, just like (*ip) IS an integer,
*this IS your class-type, not a pointer to it. its 'dereferenced'.
this (without the *) is, indeed, a pointer.

This is just a syntax rule. I don't like it either; in fact I go out of my way to use [] notation:
ip[0] = 13; //same as above *ip = 13 but I like it better.

Last edited on
pointer due to the '*'.

If '*' means "pointer", then what do I have here?
int answer = 2 * 21;
That '*' has nothing to do with a pointer. Different context, different meaning.

Binary operator* is usually multiplication.

Unary operator* is dereference operation.
1
2
3
foo = *bar; // dereference bar to get value pointed to by bar
foo = *(bar+0); // dereference bar to get value pointed to by bar
foo = bar[0]; // dereference bar to get value pointed to by bar 


The * in type means that the object is a pointer.



1
2
3
4
5
Foo & Foo::operator= (/*param*/)
{
  // code
  return *this;
}

The function returns Foo &. A reference to type Foo object. The Foo is not a pointer.

The return statement returns a value/object.

The this is a pointer. Its type is Foo*.
If we dereference type T* object, we do get object of type T object.

The *this has thus type Foo.
Thanks guys.

So let me try this again:

1
2
3
4
Obstacle & Obstacle::operator= (const Obstacle & other){
  segs=other.segs;
  return *this;
}


Here I overload the "=" operator for Obstacle types.
"Obstacle::operator=" is technically the name of the function.
Its parameters are references to objects of type Obstacle. The "const" declares that the passed object will not be changed within the function.
The return type is a reference to an Obstacle.

The function is technically a member function of an Obstacle object.
Unlike with "ordinary" functions, I don't call this method via objectname.functionname(other_object), but via objectname=other_object.

The function copies the "segs" vector of the passed object to the "segs" vector of the calling object.
Then it returns a reference to the (now manipulated) calling object via return *this.
"This" is a pointer to the calling object, i.e "This" is of type "Obstacle*".
*This is the dereferencing "This" and it gives an object of type "Obstacle".

Correct thus far?
Last edited on
yes, i think everything you said is correct now.

you actually CAN directly call the operator if you find you MUST. Normally it is as you said (x = y;) but its possible to call it via x.operator=(y) or something close to this syntax (fuzzy memory). It is very unusual to need to do it that way; I have done it that way maybe twice in 30 years... I can't even recall why, I just know it didn't compile for some reason (vexing parse of some sort?). Could have been a bug in the tool. It was long ago.

Last edited on
Alright.

Another question is:
If I declare the return type to be a reference, it looks like I do not need to make sure that the returned object is actually just a reference.
"*this" is an actual object, but due to the function signature, it gets treated as a reference of an object by the caller?
In the return statement, the reference is initialised.

A reference to an object type T can be initialised with an object of type T.
A reference variable (reference to object type T) is just an alias for an object of type T.
1
2
3
4
5
6
7
8
9
10
11
12
// A
Foo foo;
Foo& bar = foo;
gaz = bar;

// B
struct Foo {
  Foo& func();
};

Foo foo;
gaz = foo.func();

Case A creates a reference (bar) to object (foo) and then assigns reference bar to gaz.
Case B assigns reference to gaz.

We can clearly create a reference to object. We have to; you can't have a reference to nothing.
(You could Foo foo; Foo& bar = foo; Foo& gaz = bar; Foo& fubar = gaz;, but you rarely do.)
Case B has essentially unnamed temporary reference that is equivalent to 'bar' in Case A.

We don't know/care how Foo::func() conjures up a reference as long as it is valid.
This is not valid:
1
2
3
4
Foo& Foo::func() {
  Foo snafu;
  return snafu;
}

(Do you know why?)
Thank you!

@Keskiverto,

What did you mean by "you rarely do"? Aren't this legit usages of references?

Also this structure...
1
2
3
struct Foo {
  Foo& func();
};

A structure named foo that holds a function that returns a reference to an object foo? Is this legit? Well, why not I guess...


1
2
3
4
Foo& Foo::func() {
  Foo snafu;
  return snafu;
}

We don't know/care how Foo::func() conjures up a reference as long as it is valid.
This is not valid:
(Do you know why?)

I am not sure I understand this syntax. Are you defining a function func() that is supposed to belong to structure foo OUTSIDE of the structures definition?
If so, no I don't see what goes wrong. It is the same as in my code, the function's signature expects a returned reference, but the function itself returns an object that has not been declared to be a reference.

Last edited on
1
2
3
4
Foo foo;
Foo& bar = foo;
Foo& gaz = bar;
Foo& fubar = gaz;

is harder to follow than
1
2
3
4
Foo foo;
Foo& bar = foo;
Foo& gaz = foo;
Foo& fubar = foo;

In the first we do create reference from reference (gaz from bar and fubar from gaz).
The second creates all references from the original object.

Neither seems beneficial; why have so many aliases for same object?

Incidentally, we are quite likely to have function that takes argument by reference and calls another function with that argument. The other function(s) take by reference too. In that sense "reference from reference" might be surprisingly common.

1
2
3
struct Foo {
  Foo& func();
};

Lets do:
1. Change struct to class (no real change)
2. Rename Foo
3. Add a parameter for func
4. Rename func

For example:
1
2
3
4
class Obstacle {
public:
  Obstacle & Obstacle::operator= (const Obstacle & other);
};

Is that not a structure that has a member function that returns a reference? Quite legit.

I did not say which objects reference does the Foo::func() return. That was irrelevant. The point was that in both A and B a reference was assigned to gaz.


On the last example ... func() being a member function was not important. Unnecessary confusion. My bad.

Again, what is wrong in:
1
2
3
4
Bar& fubar() {
  Bar snafu;
  return snafu;
}

What is the lifetime of object snafu?
It is created and destroyed within the function call.
When the call to fubar() has returned, the snafu does not exist any more.
However, the caller could store the returned reference.

What happens, if you try to access an object that does not exist any more?
Ah, I understand your points, thank you!

The last one is subtle.
So the difference to my return of "*this" is that "*this" is the calling object itself, so it is still there after the function returned something and the object that is received on calling side is a legit reference?
Indeed.
Thank you keskiverto!

So, one question remains:
Do I even need this function in my code?

Peter87 mentioned earlier:
It looks like it's doing the exact same thing as the compiler generated copy assignment operator would have done, except that it prevents the move constructor and move assignment operator from being automatically generated.


What does the last part mean?
If I simply want to create another "obstacle" that is identical to another existing "obstacle" I can just use the standard "=" operator and remove that function, right?
There are some member functions that the compiler generates for a class in some conditions.

There are situations when you have to supply them, because compiler's versions would not be appropriate.
See https://en.cppreference.com/w/cpp/language/rule_of_three

Move contructor and move assignment operator did appear in C++11.


If I simply want to create another "obstacle" that is identical to another existing "obstacle" I can just use the standard "=" operator

What you have defined is copy assignment operator.

You do use it like:
1
2
3
Doctor strange;
Doctor house;
house = strange; // copy assignment 


I hope you did not mean the '=' in:
1
2
3
Doctor strange;

Doctor house = strange; // not an assignment 


The Rule of Zero/Three/Five tells whether you need your version (of assignment), or if the compiler's version is good enough.
Last edited on
Topic archived. No new replies allowed.
Pages: 12