Please clarify const qualifier propagation - c++

Suppose we have a following piece of code:
class Base {
public:
int a = 5;
};
class Derived : public Base {
public:
Base *parent_ = new Base;
Base* parent() const { return parent_; }
};
void f(const Derived *derived) {
Base *p = derived->parent();
p->a = 10; // <- is this correct?
}
Personally I think here is a problem:
In function f we take a pointer to const object of class Derived. This makes each member of it also const thus parent_ becomes const Base *. If it is const we should not have an ability to modify the object on which the pointer points.
Where am I wrong?

This makes each member of it also const thus parent_ becomes const Base *.
No, the pointer will become const itself, not the object it points to. So parent_ becomes Base * const, not const Base *, and your code is valid.

Base* parent() const { return parent_; }
Unfortunately, this is problem with C++. Method is const, but it is returning a non-const pointer, which would allow following operation to succeed:
p->a = 10; // <- is this correct?
It is programmer's responsibility not to return non-const pointer or references from functions (method being const or not).

As tobi303 points out below, this answer is wrong. Keeping it here for the sake of the discussion below.
I could be wrong, but this line:
Base* parent() const { return parent_; }
does not mention anything about returning const objects. It is equivalent to
Base* parent() const { return this->parent_; }
where this is const Derived*, but parent_ is still Base*.

A const method only prevents the method from modifying any non-mutable data members of the class.
If you want to not allow the modification of a and don't want to update the class, you need to write:
const Base *p = derived->parent();
However, I would recommend you to not return non-const data members from a class as ref or pointer because this breaks the data encapsulation concept of C++. This is the same, of course, with the public a.

Related

Pass Interface object to constructor c++?

Say i have an interace like
#include<iostream>
struct Interface
{
virtual void fun() = 0;
};
struct IType : Interface
{
void fun() override
{
std::cout<<"non const fun()";
}
};
class Type
{
private:
Interface &intr; // I can't declare intr as const i need to ascess non const fun()
public:
// I put it as a const for default value
Type(const Interface& obj = IType()) : intr{const_cast<Interface&>(obj)} // Is const_cast is safe or how to do it ?
// What is god idea to do it should i need to remove const and default value but i need a default constructor
{
intr.fun();
}
};
int main()
{
Type obj;
return 0;
}
Thanks.
Reference as class member is rarely a good idea. You need an object, passed from the outside, that will outlive your class object. You cannot use default argument and you cannot use a temporary for initialization. Typically, if you need to pass different types that inherit from an interface, this is solved with (smart) pointers.
Type(const Interface& obj = IType()) : intr{const_cast<Interface&>(obj)}
//Is const_cast is safe or how to do it ?
It depends. You can cast away constness only if the real object is not const. So this:
IType itype{};
Type {itype};
would be valid, but this:
const IType itype{};
Type {itype};
would be invalid.
Your default argument is invalid anyway, because that default is a temporary object that dies immediately once constructor finishes, which means you cannot use intr member outside of this constructor.
I'm not 100% sure if const_cast is valid on temporary or not, but that doesn't matter in this case.
The solution is to use (smart) pointers:
class Type
{
private:
std::shared_ptr<Interface> intr;
public:
Type(std::shared_ptr<Interface> obj = std::make_shared<IType>()) : intr{std::move(obj)}
{
intr.fun();
}
};
The default choice for smart pointer is std::unique_ptr, but use of reference suggests that you wanted access to object from the outside of the class as well, and std::shared_ptr will allow that.

Calling non-const function of another class by reference from const function [duplicate]

This question already has answers here:
Why can I call a non-const member function pointer from a const method?
(5 answers)
Closed 3 years ago.
Imagine the following example:
class A
{
public:
void doSomeStuff() { std::cout << "SomeStuff" << std::endl; }
};
class B
{
public:
B(A& a) : a(a) {}
void constStuff() const { a.doSomeStuff(); }
private:
A &a;
};
If doSomeStuff() would change the data, wouldn't that affect class B as well? Why is such behaviour allowed?
If doSomeStuff() would change the data, wouldn't that affect class B as well?
Well, not in the way a compiler checks for const correctness. A B holds a reference to an A. That object can reside anywhere, but most importantly it doesn't reside inside the B object. So modifying it does not do something with undefined behavior like changing a const object. We have a reference to a non-const object, so it's possible to modify the object via the reference. That's as far as the C++ type system cares, whether or not the object is maybe physically const.
It will probably affect the logical state of the B, but it is the responsibility of the programmer to ensure the class invariants hold. C++ will not hold your hand in that endeavor.
The original object of the class A will be changed.
When you are using a const member function then the function deals with const T *this where T is the class type.
That is data members of the object are considered constant.
For a referenced type it could look like
A & const a;
However references themselves can not be constant.
That is for example this declaration
int x;
int & const rx = x;
is invalid and does not mean the same as
const int & rx = x;
So the class B has a reference that points to a non-constant object and using the reference the object can be changed.
Compare with the following declaration of the class B
class B
{
public:
B(A * a) : a(a) {}
void constStuff() const { a.doSomeStuff(); }
private:
A *a;
};
Then then a constant member function is used the data member is considered like
A * const a;
(pointers themselves may be constant) that is the pointer itself that is constant not the object pointed to by the pointer and you can not change the pointer itself but you can change the object pointed to by the pointer.
The const-qualified this pointer that's present in a const-method protects data changes from members inside B. B holds a reference to A so the object a references does not reside inside B and thus changes are allowed. This const qualification of this does not pass on to the reference.
If you would want to avoid that, you're supposed to make the reference to the helper class const:
class B
{
public:
B(const A& a) : a(a) {}
void constStuff() const { /* Invalid call: a.doSomeStuff(); */ }
private:
const A &a;
};

const correctness and member pointers

I have const on the Accelerate method, yet I can call the power method. Is their a way to actually stop this.
class Engine{
public:
void Power(){
}
};
class Car{
public:
Car() : p(new Engine()){}
void Accelerator() const{
p->Power();
}
private:
Engine* p;
};
For Car, a const method is a methods which does not modify Car member variables.
so, as long that Car::Accelerator does not make p point to a different location, it is valid.
since p is not modified in Accelerator (meaning it does not point to a different memory address), the program is valid.
the moment you make p point to a different location, the program fails to compile:
void Accelerator() const {
p= nullptr; //wrong
}
Const protection affects only the direct members, in this case the pointer only. Values outside this object (aka. pointed values) are not protected.
You have to decide if you consider the pointed value as yourself or someone else.
You can modify the member Engine as const*, which will make the Engine object pointed to by p in Car const:
class Engine{
public:
void Power(){
}
};
class Car{
public:
Car() : p(new Engine()){}
void Accelerator() const{
p->Power();
}
private:
Engine const* p;
};
This will no longer compile:
test_const.cpp:12:9: error: member function 'Power' not viable: 'this' argument
has type 'const Engine', but function is not marked const
p->Power();
^
test_const.cpp:3:10: note: 'Power' declared here
void Power(){
^
But this implies that no member of Car can modify *p, which is probably not what you intend. See #NathanOliver's comment for better answer.
Hope this helps.

What are the properties of a const member function's return value, being a pointer?

class B
{
public:
A* GetA() const { return obj; }
private:
A* obj;
}
...
B b;
b.GetA()->AInterfaceMethod(params);
So my questions are:
What would be different had the function not been const?
Are there any restrictions to what I can do with the pointer obtained via GetA()? Is it const? Does it point to a const A?
When is this useful?
I encountered this in an Unreal tutorial (A is forward declared in the tutorial).
EDIT: I probably messed up, but the call does work. I've included the actual Unreal code below:
class ABatteryPickup : public APickup
{
ABatteryPickup()
{
GetMesh()->SetSimulatePhysics(true);
}
}
class Pickup
{
public:
class UStaticMeshComponent* GetMesh() const { return PickupMesh; }
private:
class UStaticMeshComponent* PickupMesh;
}
UStaticMeshComponent::SetSimulatePhysics() is not const.
Also, just tested this on a new, clean C++ project, and it works.
What would be different had the function not been const?
If the function is a non-const member function, you can't call it on a const object.
const B b;
b.GetA(); // Fail, can't call a non-const member function on a const object
Are there any restrictions to what I can do with the pointer obtained via GetA()? Is it const? Does it point to a const A?
No, nothing is different here. The returned value is not const itself, and not a pointer to const, it's just A* as it declared. If the object is const, the member variable obj will be const too, i.e. A* const, note it's still not a pointer to const, i.e. const A*. Anyway you return it by value with type A*, so nothing is different here.
When is this useful?
Const member function and non-const member function could be overloaded. You might use them for different purpose. e.g.
class B
{
public:
A* GetA() { return obj; } // returns A*
const A* GetA() const { return obj; } // returns const A*
private:
A* obj;
}
...
B b;
b.GetA(); // get A*
const B cb;
cb.GetA(); // get const A*
What would be different had the function not been const?
const method has a contract - it does not change internal state of object B directly or indirectly, for example it would not change pointer obj to point somewhere else. So this code:
A* GetA() const { obj = new A; return obj; }
would fail to compile.
Are there any restrictions to what I can do with the pointer obtained via GetA()? Is it const? Does it point to a const A?
No, object of type A is unrelated and to make this method to return pointer of const A you need to change type of the returned pointer:
const A *GetA() const { return obj; } // now you cannot change A through pointer you get, unless you const_cast it.
When is this useful?
You can control separately what you can change inside method and what can be done with object, pointer to which you return. You just have choice.

Bitiwise Const and pointers:

I read a line in Meyers:
"A member function that modifies what a pointer points to frequently doesn`t act const.
But if only the pointer is in the object, the function is bitwise const, and compilers wont complain."
I fail to understand that modifying a pointer in a function cannot maintain its bitwise constantness since its a member variable...
Even if we assume that bitwise constantness is only for values that pointers point to and not for the pointer addresses themselves..
Then why does it matter if its the only member variable in the class or if its not the only only member variable..
Basically this means that if you had
struct Foo
{
int bar;
};
you couldn't have a const member function change the value of bar.
However if bar is a pointer to an int, you could change the value of the int in a const method because the int is not actually part of the struct.
Both versions achieve the same goal (i.e. change the value of the int) but in the first version you are breaking bitwise constness and the compiler will complain, in the second it wont.
It's bitwise const because the member function only
modifies what [the] pointer points to
so the object instace data (the member pointer) doesn't change only the object on the heap that it points to.
Take the following simple classes:
class A
{
public:
A(int d = 0) : data(d) {}
void func() const
{
++data; // Can't do this. That's changing
// the bitwise content of this.
}
private:
int data;
};
And
class B
{
public:
A(int d = 0) : data(new int(d)) {}
void func() const
{
++(*data); // Can do this. That doesn't change
// the bitwise content of this.
}
private:
int* data;
};
If I read it correctly, a member function usually isn't qualified const if it modifies a value pointed at by a pointer stored in the current object:
class A {
char* string;
public:
void UpCaseString() { strupr(string); }
....
}
The UpCaseString() method modifies data which 'belong' to the object, so it usually would not be declared as const. However it actually modifies some buffer allocated outside the current object, the instance of A class has only a char* pointer to the buffer—so the buffer can still be modified even when the object itself is const:
class A {
char* string;
public:
void UpCaseString() const { strupr(string); }
....
}
void foo(A const &a) {
a.UpCaseString();
}
Member a.string is not modified here.