2

A pointer to a const object doesn't allow to change the object. For example:

class foo {
    public:
        int m_data;
        
        foo() : m_data{11} {}
};

void change(const foo *obj)
{
    obj -> m_data = 21;   // gives error, normal
}

This is fine and normal behavior.

What I don't understand is that when we add an additional pointer variable, either of the same type (like foo *) or of some other type (like bar *), but not the basic types like int *, then we can change the value that the pointer points to (from a pointer to the const type itself).

How is this possible?

class foo {
    public:
        int m_data;
        foo *m_next;
        
        foo() : m_data{11}, m_next{nullptr} {}
};

void change(const foo *obj)
{
    obj -> m_next -> m_data = 21;   // does not give any error! why?
}

This also happens if we take some other type inside foo, like bar *.

Why is this happening? How can I define the function so that I can be sure that my function can't change the value the object is pointing to?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    You're not changing `m_next` or anything else in `*obj`. The `foo` being `const` means that you couldn't, for instance, reassign `m_next` to point to something else. See [here](https://stackoverflow.com/questions/21476869/constant-pointer-vs-pointer-to-constant) or similar questions for more on the distinction. – Nathan Pierson May 14 '21 at 23:17
  • 1
    `const`ness is not transitive – Eljay May 14 '21 at 23:21
  • 1
    About how to make a deep-const pointer: [How to make a deep-const pointer](https://stackoverflow.com/questions/61505046/how-to-make-a-deep-const-pointer) – Ranoiaetep May 14 '21 at 23:38

2 Answers2

1
obj -> m_next -> m_data = 21;   // does not give any error! why?

Because this is not changing the object. This object's m_next pointer is exactly the same as it was before.

This is only changing whatever m_next is pointing to. Which has nothing to do, whatsoever, with the object that owns this m_next pointer.

Now, if, on the other hand, you try to assign something to the m_next pointer itself, instead of whatever it's pointing to, then you'll get the error you were expecting.

The only thing that the object contains are its enumerated members. None of them are changed by this line of code.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

In your example, foo* m_next is a pointer member variable to a mutable foo object. The pointer itself is considered part of the object instance, not the object being pointed to.

By changing m_next, you are changing the pointer, which is considered part of the instance of foo. Doing something like obj->m_next = nullptr; in change would be invalid because you are changing the memory that is part of the constant foo instance. (Note: don't don't set random pointers to null in real code, unless you're sure it's safe, or it's a possible memory leak).

By accessing the object being pointed to by m_next via m_next->, you are accessing memory of another object that is not part of the const instance passed to change. This is perfectly valid.

You could say that constness is not transitive; a const object doesn't make objects accessed through it (via pointers) const.

Stewart Smith
  • 1,396
  • 13
  • 28