This question already has answers here:
C++ const method on non const pointer member
(4 answers)
Closed 3 years ago.
#include <iostream>
#include <memory>
class B
{
public:
B(){}
void g() { }
};
class A
{
public:
A()
{
ptr_ = std::make_shared<B>();
}
void f() const
{
ptr_->g(); // compile
//obj_.g(); // doesn't compile as expected
}
std::shared_ptr<B> ptr_;
B obj_;
};
int main()
{
A a;
a.f();
}
I am surprised that this piece of code builds fine. In A::f(), I call a non-const method of a data member. When this data member is a pointer it builds, if it is not a pointer it doesn't build as expected because B::g() is non-const.
Do you understand why I am able to call a non-const function inside a const function ?
The point is who is const in the const member function, the pointer ? the pointee ?
In the const member function f, ptr_, i.e. the pointer itself is considered as const, but not the object pointed by it. You're calling non-const member function g on the pointee, then it's fine.
Furthermore, you can't perform any modification (and call non-const member function) on the pointer ptr_ itself (same as obj_), like ptr_ = std::make_shared<B>();; but you can do this on the object pointed by it, like *ptr_ = B{};.
Related
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;
};
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.
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.
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.
I am not sure whether I am missing something basic. But I am unable to understand why the compiler is generating the error for this code:
class A
{
};
class B
{
public:
B();
A* get() const;
private:
A* m_p;
};
B::B()
{
m_p = new A;
}
A* B::get() const
{
//This is compiling fine
return m_p;
}
class C
{
public:
A* get() const;
private:
A m_a;
};
A* C::get() const
{
//Compiler generates an error for this. Why?
return &m_a;
}
EDIT: The compiler error is : error C2440: 'return' : cannot convert from 'const class A *' to 'class A *' Conversion loses qualifiers
const in the function signature tells the compiler that the object's members may not be modified. Yet you return a non-const pointer to a member, thus allowing a violation of that promise.
In your class B, you make/break no promise since you don't return a pointer to a member, you return a copy of it (and the member happens to be a pointer).
It's because you're returning a non-const pointer to a member from a const function.
The first part works because you're returning a copy of a member pointer, so this doesn't violate the const-ness of the get function:
class B
{
public:
B();
A* get() const;
private:
A* m_p;
};
A* B::get() const
{
//This is compiling fine
return m_p;
}
But the next bit generates the compile error (on gcc 4)
testfile.cpp:37: error: invalid conversion from ‘const A*’ to ‘A*’
Because your const get function is providing non-const acess to m_a by returning a non-const pointer to it.
class C
{
public:
A* get() const;
private:
A m_a;
};
A* C::get() const
{
//Compiler generates an error for this. Why?
return &m_a;
}
Because the returned pointer is not const. Change it to this:
class C
{
public:
const A* get() const;
private:
A m_a;
};
const A* C::get() const
{
//Compiler generates an error for this. Why?
return &m_a;
}
Notice that C::get() now returns a const pointer to A.
Member functions marked const cannot return a non-const reference or pointer to a private variable. If the compiler allowed this, anyone outside your class would be able to modify the said private variable and the const qualifier on the function would lose meaning.
This problem can be illustrated with a simpler example:
class MyClass {
public:
int *get() const;
private:
int value;
};
int *MyClass::get() const {
return &value;
}
In MyClass::get() const, value has the type const int. When you dereference it, you get const int *. That type cannot be safely (implicitly) casted to int *. To correct your problem, have get() return const int *.
A* C::get() const
{
//Compiler generates an error for this. Why?
return &m_a;
}
Because get() is a const function, the compiler treats all member variables it refers to as const. When you take the address of such a member, you get a pointer to const. But your function is returning a non-const pointer. You need to change your code to
const A* C::get() const
{
return &m_a;
}
Basically just add a const in front,
const A* C::get() const
{
//Compiler generates an error for this. Why?
return &m_a;
}
Then if you want to access it, basically do:
C something;
const A* a = something.get();
However, your program makes very little sense, to me.
IMO, it would make most sense to do:
class A{
};
class C : public A
{
};
That way you don't have to make a "get" that returns the instance of A.