Can a heap-allocated object be const in C++? - c++

In C++ a stack-allocated object can be declared const:
const Class object;
after that trying to call a non-const method on such object is undefined behaviour:
const_cast<Class*>( &object )->NonConstMethod(); //UB
Can a heap-allocated object be const with the same consequences? I mean is it possible that the following:
const Class* object = new Class();
const_cast<Class*>( object )->NonConstMethod(); // can this be UB?
is also undefined behaviour?

Yes. It's legal to construct and destroy a const heap object. As with other const objects, the results of manipulating it as a non-const object (e.g. through a const_cast of a pointer or reference) causes undefined behaviour.
struct C
{
C();
~C();
};
int main()
{
const C* const p = new const C;
C* const q = const_cast<C*>(p); // OK, but writes through q cause UB
// ...
delete p; // valid, it doesn't matter that p and *p are const
return 0;
}

In your heap example, new returns a pointer to non-const. The fact that you've stored it in a pointer to const (and then const_casted it back to a pointer to non-const) doesn't change the fact that the object itself is not const in the same way as the stack-allocated one is.
However, you can create a const object on the heap:
const Class* object = new const Class();
In such a case, casting to a pointer to non-const and calling a non-const method would be the same situation as the const stack-allocated object.
(The idea of creating a const object on the heap was new to me, I had never seen that before. Thanks to Charles Bailey.)

Yes, a heap-allocated object can be const. Consider this excerpt from the example in 7.1.5.1/5:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
The example you gave in the question is fine because you're not asking new to make a const object; you're just storing the result in a pointer-to-const.

Don't forget mutable members
It won't be undefinied behaviour if the NonConstMethod only modifies mutable qualified members (see 7.1.5.1 (4)) of a const qualified class. Yes, otherwise it's undefined behaviour.
const A* p = new(const A);
A *q = const_cast<A*>(p);
q->NonConstMethodThatModifiesMembers(); // undefined behaviour!
q->NonConstMethodThatOnlyModifiesMutableMembers(); // defined behaviour!

Obviously:
struct Foo {
const int Bar;
Foo() : Bar(42) { }
};
Foo* foo = new Foo;
const_cast<int&>(foo->Bar); // don't do this.

const_cast can cause UB when the object is actually read-only (for example, the compiler can create such objects when you use hard coded strings in your code, by placing them in certain memory areas that are read only) for some reason. This will not happen with heap allocated objects, no matter how you keep their reference (const pointer, const reference, whatever).

Related

UB when changing non const data of const pointer [duplicate]

Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).

Is it UB to Modify where pointer points, when original data is const?

Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).

const reference to a pointer can change the object

const references make sure you can't change the object you're referring to. For example:
int i = 1;
const int& ref = i;
ref = 42; // error, because of a const reference
But if you use a reference to a pointer or a unique_ptr, you can. Example:
class TinyClass {
public:
int var = 1;
void f1() { var = 42; }
};
std::unique_ptr<TinyClass> pointer(new TinyClass);
const std::unique_ptr<TinyClass>& constRef = pointer;
constRef->f1(); // no error
I assume this happens because the pointer itself wasn't changed. But this feels misleading, or like an easy mistake. Is there a simple way to declare a "real" const reference to a pointer? As in: makes sure the object itself is const.
edit: Assume that I can't just change the type of the unique_ptr. A real-world scenario would be that some objects are constructed in a vector<unique_ptr<C>> and some function gets a (const) reference to one of its elements.
The const in const std::unique_ptr<TinyClass>& constRef guarantees that constRef will not point to another object once it set up. It has nothing to do with the object itself.
If you want to prevent changing the object itself:
std::unique_ptr<const TinyClass> ptr_to_const_object;
Edit (after OP's edit):
You can not do anything. Since there is a function which wants const vector<unique_ptr<C>>&, the function clearly tells you that it needs to play with the object inside the pointer (and even the pointer) but it does not need to change the vector items (like adding new item or deleting it).

const-pointer to non-const-pointer in constructor

I'm curious why the compiler doesn't complain if I hand over a const-pointer to a non-const-pointer as parameter in a constructor of a class that itself, which of course is at construction time not const.
#include <iostream>
class A
{
public:
A(int* v) : v(v) {}
int* v;
};
class B
{
public:
B() : o(23), v(&o) {}
const A cc() const
{
return A(v); //expected const to non-cosnt error here, but seems valid
}
int o;
int* v;
};
int main() {
B b;
const B* bc = &b;
A a = bc->cc();
*(a.v) = 25; // here I'm changing an originally const value
std::cout << b.o << std::endl;
return 0;
}
With defining cc() as const I expected the line where the return value is initialized an error message about converting a const to a non-const. The code-snippet compiles fine and in the end I get the output "25" even so that should have been const.
The A constructor expects a copy of a pointer instead of a reference. It is absolutely legal to copy a int * const into an int*. Therefore, no error occurs.
You should use references instead of pointers when you want a consistent behavior regarding constness. By the way, returning const objects is a bad idea.
You're confusing int const* (or const int*) and int* const.
Copying a const object into a non-const new object is fine, whether that object is a pointer or not. That's all you're doing here; the pointer itself is const (it's int* const), so you can pretty much do what you like with a copy of it.
What is not ok is copying a int const* into a int*, because now you're saying the thing being pointed to has magically lost its const-protection.
constness of an object does not propagate to constness of the pointee of a pointer member. The pointer becomes const but the pointee stays non-const. Even if the pointer is const, you can easily obtain a non-const pointer by simply copying the pointer which is exactly what happens as the pointer is passed by value.

Referencing const objects/pointers in C++

There's something I don't quite understand with how references are handled in C++:
B objB = B(); // Regular B object
const B &refConstObjB = objB; // Reference to const B object
B* ptrB = new B(); // Regular pointer to B
B* const &refPtrConstB = ptrB; // Reference to const pointer to B
All of the above compiles just fine. However the following doesn't:
const B* &refConstPtrB = ptrB; // Reference to pointer to const B
Considering both the object and the pointer were declared as non-const, why can I reference the object as a const object, but can't do the same with the pointer?
Just to note: A const reference does not imply a const object.
It simply means an object that is read-only through that reference. So whether or not an object is const, you can have a read-only reference or pointer to it.
Now, if what you mentioned were allowed, you could say refConstPtrB = pointer_to_some_const_B, and then mutate that object through ptrB, which would violate the const-ness of the pointer's target.
The reason for this error is that you can put a hole in the type system with it. Consider the following code:
const B dontChangeMe;
B* ptrB = new B();
/* This step is NOT legal. */
const B* &refConstPtrB = ptrB;
/* Uh oh! I now have ptrB pointing at dontChangeMe! */
refConstPtrB = &dontChangeMe;
Consequently, you can't do the assignment that you marked.
Hope this helps!