Why can delete operator be used in const context? - c++

This question is different from:
Is a destructor considered a const function?
new-expression and delete-expression on const reference and const pointer
Deleting a pointer to const (T const*)
I wrote a class Test like this.
class Test {
private:
int *p;
public:
//constructor
Test(int i) {
p = new int(i);
}
Test & operator = (const Test &rhs) {
delete p;
p = new int(*(rhs.p));
return *this;
}
};
When the parameter rhs of the operator function is itself (i.e. Test t(3); t = t;), delete p; also changes the pointer p of rhs. Why is this allowed?
C++ standard (N3092, "3.7.4.2 Deallocation functions") says
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined.
(Note: delete-expression internally calls a deallocation function. So this excerpt is related with delete operator.)
So I think delete p; may change the member p of rhs though rhs is a const reference.
Someone may insist that "to render a pointer invalid is not to change the value of a pointer" but I don't find such a statement in the standard. I doubt there is a possibility that the address pointed by rhs's p has been changed after delete p; in operator =(*).
(*): Whether or not this situation can be reproduced on popular compilers doesn't matter. I want a theoretical guarantee.
Supplement:
I've changed delete p; to delete rhs.p;, but it still works. Why?
Full code here:
#include <iostream>
class Test {
private:
int *p;
//print the address of a pointer
void print_address() const {
std::cout << "p: " << p << "\n";
}
public:
//constructor
Test(int i) {
p = new int(i);
}
Test & operator = (const Test &rhs) {
print_address(); //=> output1
delete rhs.p;
print_address(); //=> output2
p = new int(*(rhs.p));
return *this;
}
};
int main() {
Test t(3);
t = t;
}
In this case, it is guaranteed that p is invalidated. But who guarantees invalidate != (change the value)? i.e. Does the standard guarantee that output1 and output2 are the same?

So I think delete p; may change the member p of rhs though rhs is a const reference.
No. delete p; doesn't change p. Invalidation is not modification.
Regardless, having a const reference to an object (rhs) does not by any means prevent the referred object form being modified. It merely prevents modification through the const reference. In this case we access the object through this which happens to be a pointer to non-const, so modification is allowed.
Someone may insist that "to render a pointer invalid is not to change the value of a pointer" but I don't find such a statement in the standard.
The behaviour of delete expression is specified in [expr.delete]. Nowhere in that section does it mention that the operand is modified.
Becoming invalid is specified like this:
[basic.compound]
... A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration ...
Note that it is the value that becomes invalid. The pointer still has the same value because the pointer was not modified. The value that the pointer had and still has is simply a value that no longer points to an object - it is invalid.
Supplement: I've changed delete p; to delete rhs.p;, but it still works. Why?
Answer 2. From previous question no longer applies, but answer 1. does. delete rhs.p; does not modify rhs.p.

Calling delete on a member pointer frees the memory the pointer points to but does not change the pointer itself. Thus, it does not change the bitwise contents of the object, thus it can be done in a const member.
C++ only cares about bitwise const (of the object the method is invoked on). Not logical const. If no bits in the object change, then all is well - const wise - as far as the C++ language is concerned. It does not matter whether the logical behaviour of the object is changed (for example by changing something member pointers point to). That's not what the compiler checks for.

Related

Why I can take address of *v.begin() where v is a std::vector

#include <vector>
#include <cstdio>
using namespace std;
int f()
{
int* a = new int(3);
return *a;
}
int main()
{
//printf("%p\n", &f());
vector<int> v{3};
printf("%p\n", &(*(v.begin())));
}
I cannot take address of the f(), If I comment "printf("%p\n", &f()); " out I will get error: lvalue required as unary ‘&’ operand.
but how is it possible to take address of *(v.begin())? Isn't * operator the same as a function?
The function f returns a temporary object of the type int
int f()
{
int* a = new int(3);
return *a;
}
You may not apply the address of operator for a temporary object.
You could return a reference to the created dynamically object like for example
int & f()
{
int* a = new int(3);
return *a;
}
And in this case this call of printf written like
printf("%p\n", ( void * )&f());
will be correct.
As for this expression &(*(v.begin())) then the dereferencing operator does return a reference to the pointed object by the iterator.
I cannot take address of the f(), If I comment "printf("%p\n", &f()); " out I will get error: lvalue required as unary ‘&’ operand
We cannot take the address of f() because f returns by value which means that the expression f() is an rvalue of type int which can't be the operand of the & since the operator & requires an lvalue operand which f() is not. This is exactly what the error says.
how is it possible to take address of *(v.begin())?
On the other hand, std::vector::begin returns an iterator to the first element of the vector(in case vector is non empty). Then applying * on v.begin() gives us that first element itself( i.e *(v.begin()) is an lvalue) whose address we can take.
As mentioned in the comments dereferencing interators returns a reference, i.e. a construct similar to a pointer (technically, under the hoods) – you now can take the address of *v.begin() (you don't need parentheses around, . has higher precedence than * anyway) because taking the address of a reference means taking the address of the referred object, which in this case is the first int of the vector. This is just the same as taking the address of a normal object or of the first element in an array, as if, within f, you did &a[0].
On the other hand f itself returns a value – this is just a temporary object that doesn't have a valid address (potentially at least – the value might be returned, according to calling convention, on the stack, then it does have an address, but as well in a CPU register, then there simply is no adress). This value first needs to be assigned to a variable so that you indeed can take the address of.
Indeed v.begin() returns rvalue iterator so &v.begin() will not work. But dereference operator of itirator *v.begin() returns const int& which in turn is lvalue reference what allows to call addressof on it.

Why do we return *this in asignment operator and generally (and not &this) when we want to return a reference to the object?

I'm learning C++ and pointers and I thought I understood pointers until I saw this.
On one side the asterix(*) operator is dereferecing, which means it returns the value in the address the value is pointing to, and that the ampersand (&) operator is the opposite, and returns the address of where the value is stored in memory.
Reading now about assignment overloading, it says "we return *this because we want to return a reference to the object". Though from what I read *this actually returns the value of this, and actually &this logically should be returned if we want to return a reference to the object.
How does this add up? I guess I'm missing something here because I didn't find this question asked elsewhere, but the explanation seems like the complete opposite of what should be, regarding the logic of * to dereference, & get a reference.
For example here:
struct A {
A& operator=(const A&) {
cout << "A::operator=(const A&)" << endl;
return *this;
}
};
this is a pointer that keeps the address of the current object. So dereferencing the pointer like *this you will get the lvalue of the current object itself. And the return type of the copy assignment operator of the presented class is A&. So returning the expression *this you are returning a reference to the current object.
According to the C++ 17 Standard (8.1.2 This)
1 The keyword this names a pointer to the object for which a
non-static member function (12.2.2.1) is invoked or a non-static data
member’s initializer (12.2) is evaluated.
Consider the following code snippet as an simplified example.
int x = 10;
int *this_x = &x;
Now to return a reference to the object you need to use the expression *this_x as for example
std::cout << *this_x << '\n';
& has multiple meanings depending on the context. In C and used alone, I can either be a bitwise AND operator or the address of something referenced by a symbol.
In C++, after a type name, it also means that what follows is a reference to an object of this type.
This means that is you enter :
int a = 0;
int & b = a;
… b will become de facto an alias of a.
In your example, operator= is made to return an object of type A (not a pointer onto it). This will be seen this way by uppers functions, but what will actually be returned is an existing object, more specifically the instance of the class of which this member function has been called.
Yes, *this is (the value of?) the current object. But the pointer to the current object is this, not &this.
&this, if it was legal, would be a pointer-to-pointer to the current object. But it's illegal, since this (the pointer itself) is a temporary object, and you can't take addresses of those with &.
It would make more sense to ask why we don't do return this;.
The answer is: forming a pointer requires &, but forming a reference doesn't. Compare:
int x = 42;
int *ptr = &x;
int &ref = x;
So, similarly:
int *f1() return {return &x;}
int &f1() return {return x;}
A simple mnemonic you can use is that the * and & operators match the type syntax of the thing you're converting from, not the thing you're converting to:
* converts a foo* to a foo&
& converts a foo& to a foo*
In expressions, there's no meaningful difference between foo and foo&, so I could have said that * converts foo* to foo, but the version above is easier to remember.
C++ inherited its type syntax from C, and C type syntax named types after the expression syntax for using them, not the syntax for creating them. Arrays are written foo x[...] because you use them by accessing an element, and pointers are written foo *x because you use them by dereferencing them. Pointers to arrays are written foo (*x)[...] because you use them by dereferencing them and then accessing an element, while arrays of pointers are written foo *x[...] because you use them by accessing an element and then dereferencing it. People don't like the syntax, but it's consistent.
References were added later, and break the consistency, because there isn't any syntax for using a reference that differs from using the referenced object "directly". As a result, you shouldn't try to make sense of the type syntax for references. It just is.
The reason this is a pointer is also purely historical: this was added to C++ before references were. But since it is a pointer, and you need a reference, you have to use * to get rid of the *.

new-expression and delete-expression on const reference and const pointer

C++
Much literature says const references cannot be used to modify their referents and const pointers cannot be used to modify their pointees.
Then, why can they be deleted?
const int& cirDynamic = *( new int(5) );
// ^ 'const int& cirDynamic = *( &( *( new int(5) ) ) );' gives same output below
cout << cirDynamic << endl; // 5
delete &cirDynamic;
cout << cirDynamic << endl; // garbage value
I know the trailing const in T* const only prevents the pointer from being reseated, but below I use two consts, as in const T* const, for emphasis. Why can the following pointer be deleted?
const int* const cipcDynamic = new int(5);
// ^ 'const int* const cipcDynamic = &( *( new int(5) ) );' gives same output below
cout << *cipcDynamic << endl; // 5
delete cipcDynamic;
cout << *cipcDynamic << endl; // garbage value
The output shows that at least some dynamically allocated memory was freed. Has all of it been freed, or could there have been copying involved where only the copy was freed?
The non-const version of the const reference snippet (int&) and the non-leading-const versions of the const pointer const snippet (int* const and int*) produce the same output as their more const counterparts. In all 5 cases, why and how is the lifetime of the temporary new-expression extended?
Assuming the corresponding operator has not been overloaded, explicitly deleted, or made non-public if the data type is a class or struct, does the Standard make the following guarantees:
The dereference operator provides direct access to the pointee
The new operator produces a pointer to the dynamically allocated memory, not a dynamically allocated copy of the original dynamically allocated memory
If instead the new operator was overloaded but still returned ::operator new(size) and the dereference operator was overloaded but still returned a reference to the object, are there any side-effects that would make these two points not hold?
Constness affects objects themselves. new and delete and constructors affect the creation of objects. It doesn't make sense to ask whether constructors or destructors are const, because they run before or after the object exists. Similarly, you can create and destroy constant objects dynamically, and/or you can manage dynamically created objects through constant pointers or references.
As a very simple thought experiment, consider this code:
{
const int x = 0;
}
This wouldn't work if constness could prevent the object x from being destroyed.
'const' in all your examples only prevents you to modify the variable through assignment. That's all it does. It does not prevent delete to reclaim the memory.
In your first example, "const int& cirDynamic" prevents you to write something like "cirDynamic=2". But it's legal to take address of cirDynamic (which will get you a "const int*" pointer), and delete would operate on const pointers happily.
In your second example, "const int* const cipcDynamic", the first const prevents you from modifying the place pointed by the pointer, like "*cipcDynamic = 2", the second const prevents you from modifying the pointer itself to point to another place, like "cipcDynamic = new int".

C++ interview about operator

Here is the code which basically implementing the = assignment for a class named CMyString, and the code is right.
CMyString& CMyString::operator =(const CMyString &str) {
if(this == &str)
return *this;
delete []m_pData;
m_pData = NULL;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
The instance is passed by reference, and the first 'if' is checking whether the instance passed in is itself or not. My question is: why does it use &str to compare, doesn't str already contain the address of the instance? Could any one explain how this line works?
Also, I just want to make sure that this contains the address of the object: Is this correct?
isn't str already contains the address of the instance
No. A reference is the object itself. It's not a pointer to the object.
(I. e., in the declaration of the function, &str stands for "reference to str" and not "address of str" - what you're talking about would be right if the function was declared like this:
CMyString& CMyString::operator =(const CMyString *str);
but it isn't.)
Address-of Operator and Reference Operator are different.
The & is used in C++ as a reference declarator in addition to being the address-of operator. The meanings are not identical.
int target;
int &rTarg = target; // rTarg is a reference to an integer.
// The reference is initialized to refer to target.
void f(int*& p); // p is a reference to a pointer
If you take the address of a reference, it returns the address of its target. Using the previous declarations, &rTarg is the same memory address as &target.
str passed by to the assignment operator is passed by reference, so it contains the actual object, not its address. A this is a pointer to the class a method is being called on, so if one wants to compare, whether passed object is the same object itself, he has to get the address of str in order to compare.
Note, that & behaves differently, depending on where it is used. If in statement, it means getting an address to the object it is applied to. On the other hand, if it is used in a declaration, it means, that the declared object is a reference.
Consider the following example:
int i = 42;
int & refToI = i; // A reference to i
refToI = 99;
std::cout << i; // Will print 99
int j = 42;
int * pJ = &j; // A pointer to j
*pJ = 99;
std::cout << j; // Will print 99
this is a pointer to the instance, so yes, it contains the address.
The whole point of verifying, if the passed object is this or not is to avoid unnecessary (or, possibly destructive) assignment to self.
While indeed a variable reference - denoted by the symbol & after the type name - underlying implementation is usually a pointer, the C++ standard seemingly does not specify it.
In its usage anyway, at the syntax level, a reference is used like a non referenced value of the same type, ie. more strictly speaking :
If the type of the variable is T &, then it shall be used as if it were of type T.
If you must write str.someMethod() and not str->someMethod() (without any overloading of the arrow operator), then you must use & to obtain the address of the value. In other words, a reference acts more or less like an alias of a variable, not like a pointer.
For more information about references and pointers, see these questions:
What's the meaning of * and & when applied to variable names?
What are the differences between a pointer variable and a reference variable in C++?
Why 'this' is a pointer and not a reference?

What is the difference between a reference and a pointer?

Could someone please explain the difference to me in very small words? I have never understood this and am very confused in my current project. What I am trying to do is fix this code:
const Multinumber& Pairs::operator+(const Multinumber &rhs) const
{
const Pairs &_rhs = dynamic_cast<const Pairs &>(rhs);
Pairs toreturn(_rhs.X_value+X_value,_rhs.Y_value+Y_value);
return toreturn; //reference to local variable? fix this?
}
Now my compiler tells me that this is a reference to a local variable, but it won't let me turn toreturn into a pointer, because they are somehow different from references. And then, I am also working on a set class, which is supposed to hold references or pointers to objects of an abstract class. I am totally confused. Any help would be much appreciated.
First, your signature is wrong. It should be:
Multinumber Pairs::operator+(const Multinumber &rhs) const;
operator+ should return a new object, not a reference to either argument.
As for the difference between references and pointers, this detailed question right here on SO. It covers all the basics, and the some.
You do seem very confused :) Ok, so:
Essentially, a pointer is just a variable that stores the address of another variable. For instance, suppose I have an int called i, I can store its address in a pointer p:
int i = 23;
int *p = &i; // p has type int* (pointer to int) and stores &i (the address of i)
If I then want to change the thing it points to (i.e. the variable whose address it stores), I just assign to *p -- this is the syntax used to denote the thing pointed to. In this case, *p refers to i. Thus:
*p = 9; // sets i to 9 (since *p is i)
I can reseat the pointer (i.e. make it point to something else) just by assigning to p, i.e.
int j = 84;
p = &j; // store j's address in p, overwriting what was there before (i.e. i's address)
*p = 18; // sets j to 18 (since *p is now j)
Now, a reference is slightly different. A reference creates an alias for a variable:
int i = 23;
int& r = i; // r has type int& (reference to int) and refers to i
Note that references may be implemented in terms of pointers (or they may not, particularly when the compiler starts optimizing things), but that's irrelevant from the programming perspective -- all that matters to us here is the way the language works.
If you want to change the thing referred to (i.e. i in this case), you just do:
r = 9; // sets i to 9 (since r is an alias for i)
Unlike pointers, references cannot be reseated. As just shown, assigning to r changes the thing you're referring to (i), not the reference itself. Furthermore, because references cannot be reseated, they must be initialized immediately. This is not the case with pointers. In other words:
int *p; // legal
int& r; // bad
One final basic difference is that pointers can be NULL, indicating that they are not pointing to anything. This just means they contain the address 0. References must always refer to an actual object. This difference can be important to implementers, because they can then use pointers to implement a Maybe type, i.e. if the pointer is not NULL, then make use of the pointed-to object, otherwise do something else. They can't do the same thing with references.
Hope that's clear as regards pointers vs. references!
Now, regarding your operator+ -- the purpose of an addition operator is to add two objects and return a new object representing their sum. So if I had a 2D vector type, I might write an operator+ for it as follows:
Vec2 operator+(const Vec2& lhs, const Vec2& rhs)
{
return Vec2(lhs.x+rhs.x, lhs.y+rhs.y);
}
In your code, you are trying to return a local object toreturn by reference -- this doesn't work, because toreturn ceases to exist at the end of the operator. Instead, you should return by value here. Incidentally, you would encounter the same problem if you tried to return a pointer, e.g.
Vec2* operator+(const Vec2& lhs, const Vec2& rhs)
{
Vec2 result(lhs.x+rhs.x, lhs.y+rhs.y);
return &result; // bad!
}
In that code, result ceases to exist at the end of the operator, so the pointer you return would end up pointing to an invalid location. Bottom line -- don't try anything fancy, return by value in this sort of situation.
Pointer has some address where the object is. And the reference is as the alias for the pointer and it means that you don't have to dereference it. But the usage is similar - don't copy objects, only work with the origin.
You have the variable toreturn as a local variable which means that the compiler generates the destructor for this object at the end of method. So you are trying to return destroyed object.