Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 574
matn Avatar asked Mar 13 '19 16:03

matn


2 Answers

So I think delete p; may change the member p of rhs though rhs is a const reference.

  1. No. delete p; doesn't change p. Invalidation is not modification.

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

like image 173
eerorika Avatar answered Oct 16 '22 07:10

eerorika


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.

like image 32
Jesper Juhl Avatar answered Oct 16 '22 07:10

Jesper Juhl