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

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".

Related

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 *.

Meaning of references, address-of, dereference and pointer

Here is the way I understand * and & symbols in C and C++.
In C, * serves two purposes. First it can be used to declare a pointer variable like so int* pointerVariable
It can however be used as a dereference operator like so *pointerVariable which returns value saved at that address, it understands how to interpret bytes at that address based on what data type we have declared that pointer is pointing to. In our case int* therefore it reads bytes saved at that address and returns back whole number.
We also have address-of operator in C like so &someVariable which returns address of bytes saved underneath someVariable name.
However in C++ (not in C), we also get a possibility to use & in declaration of reference like so int& someReference. This will turn variable someReference into a reference, which means that whatever value we pass into that variable, it will automatically get address of the value we are passing into it and it will hold it.
Do I get this correctly?
Do I get this correctly?
Yes, but it is better to think about pointers and references in terms of what you want to do.
References are very useful for all those cases where you need to refer to some object without copying it. References are simple: they are always valid and there is no change in syntax when you use the object.
Pointers are for the rest of cases. Pointers allow you to work with addresses (pointer arithmetic), require explicit syntax to refer to the object behind them (*, &, -> operators), are nullable (NULL, nullptr), can be modified, etc.
In summary, references are simpler and easier to reason about. Use pointers when a reference does not cut it.
General Syntax for defining a pointer:
data-type * pointer-name = &variable-name
The data-type of the pointer must be the same as that of the variable to which it is pointing.
void type pointer can handle all data-types.
General Syntax for defining a reference variable:
data-type & reference-name = variable-name
The data-type of the reference variable must be the same as that of the variable of which it is an alias.
Let's look at each one of them, for the purpose of explanation, I will go with a simple Swap Program both in C and C++.
Swapping two variables by the pass by reference in C
#include <stdio.h>
void swap(int *,int *); //Function prototype
int main()
{
int a = 10;
int b = 20;
printf("Before Swap: a=%d, b=%d\n",a,b);
swap(&a,&b); //Value of a,b are passed by reference
printf("After Swap: a=%d, b=%d\n",a,b);
return 0;
}
void swap(int *ptra,int *ptrb)
{
int temp = *ptra;
*ptra = *ptrb;
*ptrb = temp;
}
In the code above we have declared and initialized variable a and
b to 10 and 20 respectively.
We then pass the address of a
and b to swap function by using the addressof (&) operator. This operator gives the address of the variable.
These passed arguments are assigned to the respective formal parameters which in this case are int pointers ptra and ptrb.
To swap the variables, we first need to temporarily store the value of one of the variables. For this, we stored value pointed by the pointer ptra to a variable temp. This was done by first dereferencing the pointer by using dereference (*) operator and then assigning it to temp. dereference (*) operator is used to access the value stored in the memory location pointed to by a pointer.
Once, the value of pointed by ptra is saved, we can then assign it a new value, which in this case, we assigned it the value of variable b(again with the help of dereference (*) operator). And the ptrb was assigned the value saved in temp(original value of a). Therefore, swapping the value of a and b, by altering the memory location of those variables.
Note: We can use dereference (*) operator and the addressof (&) operator together like this, *&a, they nullify each other resulting in just a
We can write a similar program in C++ by using pointers to swap two numbers as well but the language supports another type variable known as the reference variable. It provides an alias (alternative name) for a previously defined variable.
Swapping two variables by the call by reference in C++
#include <iostream>
using namespace std;
void swap(int &,int &); //Function prototype
int main()
{
int a = 10;
int b = 20;
cout << "Before Swap: a= " << a << " b= " << b << endl;
swap(a,b);
cout << "After Swap: a= " << a << " b= " << b << endl;
return 0;
}
void swap(int &refa,int &refb)
{
int temp = refa;
refa = refb;
refb = temp;
}
In the code above when we passed the variables a and b to the function swap, what happened is the variable a and b got their respective reference variables refa and refb inside the swap. It's like giving a variable another alias name.
Now, we can directly swap the variables without the dereferencing (*) operator using the reference variables.
Rest logic remains the same.
So before we get into the differences between pointers and references, I feel like we need to talk a little bit about declaration syntax, partly to explain why pointer and reference declarations are written that way and partly because the way many C++ programmers write pointer and reference declarations misrepresent that syntax (get comfortable, this is going to take a while).
In both C and C++, declarations are composed of a sequence of declaration specifiers followed by a sequence of declarators1. In a declaration like
static unsigned long int a[10], *p, f(void);
the declaration specifiers are static unsigned long int and the declarators are a[10], *p, and f(void).
Array-ness, pointer-ness, function-ness, and in C++ reference-ness are all specified as part of the declarator, not the declaration specifiers. This means when you write something like
int* p;
it’s parsed as
int (*p);
Since the unary * operator is a unique token, the compiler doesn't need whitespace to distinguish it from the int type specifier or the p identifier. You can write it as int *p;, int* p;, int * p;, or even int*p;
It also means that in a declaration like
int* p, q;
only p is declared as a pointer - q is a regular int.
The idea is that the declaration of a variable closely matches its use in the code ("declaration mimics use"). If you have a pointer to int named p and you want to access the pointed-to value, you use the * operator to dereference it:
printf( "%d\n", *p );
The expression *p has type int, so the declaration of p is written
int *p;
This tells us that the variable p has type "pointer to int" because the combination of p and the unary operator * give us an expression of type int. Most C programmers will write the pointer declaration as shown above, with the * visibly grouped with p.
Now, Bjarne and the couple of generations of C++ programmers who followed thought it was more important to emphasize the pointer-ness of p rather than the int-ness of *p, so they introduced the
int* p;
convention. However, this convention falls down for anything but a simple pointer (or pointer to pointer). It doesn't work for pointers to arrays:
int (*a)[N];
or pointers to functions
int (*f)(void);
or arrays of pointers to functions
int (*p[N])(void);
etc. Declaring an array of pointers as
int* a[N];
just indicates confused thinking. Since [] and () are postfix, you cannot associate the array-ness or function-ness with the declaration specifiers by writing
int[N] a;
int(void) f;
like you can with the unary * operator, but the unary * operator is bound to the declarator in exactly the same way as the [] and () operators are.2
C++ references break the rule about "declaration mimics use" hard. In a non-declaration statement, an expression &x always yields a pointer type. If x has type int, &x has type int *. So & has a completely different meaning in a declaration than in an expression.
So that's syntax, let's talk about pointers vs. references.
A pointer is just an address value (although with additional type information). You can do (some) arithmetic on pointers, you can initialize them to arbitrary values (or NULL), you can apply the [] subscript operator to them as though they were an array (indeed, the array subscript operation is defined in terms of pointer operations). A pointer is not required to be valid (that is, contain the address of an object during that object's lifetime) when it's first created.
A reference is another name for an object or function, not just that object's or function's address (this is why you don't use the * operator when working with references). You can't do pointer arithmetic on references, you can't assign arbitrary values to a reference, etc. When instantiated, a reference must refer to a valid object or function. How exactly references are represented internally isn't specified.
This is the C terminology - the C++ terminology is a little different.
In case it isn't clear by now I consider the T* p; idiom to be poor practice and responsible for no small amount of confusion about pointer declaration syntax; however, since that's how the C++ community has decided to do things, that's how I write my C++ code. I don't like it and it makes me itch, but it's not worth the heartburn to argue over it or to have inconsistently formatted code.
Simple answer:
Reference variables are an alias to the data passed to them, another label.
int var = 0;
int& refVar = var;
In practical terms, var and refVar are the same object.
Its worth noting that references to heap pointer data cannot deallocate (delete) the data, as its an alias of the data;
int* var = new int{0};
int& refVar = *var;
delete refVar // error
and references to the pointer itself can deallocate (delete) the data, as its an alias of the pointer.
int* var = new int{0};
int*& refVar = var;
delete refVar // good

Why can delete operator be used in const context?

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.

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?

Why declare a constant pointer using the const keyword when the reference (const pointer) is available?

For example:
I could make a constant pointer, which points to an object that I can change through my pointer. The pointer cannot be reassigned:
MyObj const *ptrObj = MyObj2
Why would I use this over:
MyObj &ptrObj = MyObj2
What you have there isn't a const pointer, it's a pointer to a const object - that is, the pointer can be changed but the object can't. A const pointer would be:
MyObj *const ptrObj = &MyObj2;
As to why you might prefer it over a reference, you might want the flexibility of using the NULL special value for something - you don't get that with a reference.
You got it wrong. What you have is a mutable pointer to a constant object:
T const * p;
p = 0; // OK, p isn't const
p->mutate(); // Error! *p is const
T const & r = *p; // "same thing"
What you really want is a constant pointer to mutable object:
T * const p = &x; // OK, cannot change p
T & r = x; // "same thing"
p->mutate(); // OK, *p is mutable
Indeed, references are morally equivalent to constant pointers, i.e. T & vs T * const, and the constant version T const & vs T const * const.
If you insist on getting some advice, then I'd say, "don't use pointers".
The important difference between a pointer and a reference is how many objects they may refer to. A reference always refers to exactly one object. A pointer may refer to zero (when the pointer is null), one (when the pointer was assigned the location of a single object) or n objects (when the pointer was assigned to some point inside an array).
The ability of pointers to refer to 0 to n objects means that a pointer is more flexible in what it can represent. When the extra flexibility of a pointer is not necessary it is generally better to use a reference. That way someone reading your code doesn't have to work out whether the pointer refers to zero, one or n objects.