Calling functions with or without "const" signature in C++ - c++

#include <iostream>
using namespace std;
class Object
{
public:
Object() {}
void Print() const
{
cout << "const" << endl;
}
void Print()
{
cout << "mutable" << endl;
}
};
void print_obj(const Object& obj)
{
obj.Print();
}
int main()
{
Object obj1;
const Object obj2;
Object*const pobj1 = &obj1;
print_obj(obj1);
print_obj(obj2);
obj1.Print();
obj2.Print();
pobj1->Print();
return 0;
}
I know the output is
const
const
mutable
const
mutable
I guess for const object, it looks for the const function. Otherwise the mutable function. But why is the last one mutable?

But why is the last one mutable?
Object*const pobj1 = &obj1;
declares pobj1 to be a const pointer to a non-const object. The object is still mutable but the pointer is not.
After the variable is initialized, you will not be able to change it to point to another variable.
pobj1 = &obj2; // Not OK.
But you can still change the variable it points to and call non-const member functions of the class through the pointer.
*pobj1 = obj2; // OK

But why is the last one mutable?
This is because the declaration is Object* const pobj1. Since we are working with pointers there are 2 const possibilities.
const Object* const pobj1 OR Object const * const pobj1
^^1^^ ^^2^^ ^^1^^ ^^2^^
// 1 applies to the types (i.e., Object)
// 2, which is what you're using applies to the pointer, therefore you're using a non-const Object

This
Object*const pobj1 = &obj1;
declares pobj1 to be a const pointer to a non-const object. If you doubt that,
just change it to
Object*const pobj2 = &obj2;
This won't compile because it throws away a const qualifier of obj2.
BTW, the restriction is that for const object, it can ONLY use const functions. 'const' after the parenthesis means that 'this*' pointer will be treated as a pointer to a const object within the method.
For non-const objects, it can use both const and non-const methods, but it will chose to use non-const first (if both are defined).

I believe you meant "const Object*". "const" can be applied to the pointer and/or the object the pointer is pointing to. The twist is that "Object * const" is the same as "Object const*".

Related

C++ const problems when member value is changed from pointer to non-pointer

I have two classes TestClass and OtherClass, where TestClass has a member variable of type OtherClass named m_otherClass. Note that this is not declared to be const.
In the minimal example provided below; when m_otherClass is a pointer, then everything compiles and runs fine. If I change this to be a non-pointer, then I get compiler errors (the changes are commented out in the minimal example):
"Non-const function 'setOtherMember' is called on const object"
error: passing 'const OtherClass' as 'this' argument discards qualifiers [-fpermissive] m_otherClass.setOtherMember();
#include <iostream>
#include <memory>
class OtherClass {
public:
void setOtherMember() {
m_otherMember = 2;
std::cout << "Other member is now 2" << std::endl;
}
private:
int m_otherMember = 0;
};
class TestClass {
public:
TestClass(): m_otherClass(std::make_unique<OtherClass>())
// TestClass()
{}
void myMethod() const {
m_otherClass->setOtherMember();
// m_otherClass.setOtherMember();
}
private:
std::unique_ptr<OtherClass> m_otherClass;
// OtherClass m_otherClass; // If changing to this I get the error!!
};
int main() {
TestClass testClass;
testClass.myMethod();
return 0;
}
Is this because myMethod() is const (and then promising not to change any member variables), whereas setOtherMember() is non-const and is changing OtherClass's member variable, and then indirectly also the m_otherClass object?
But why does this not fail then when m_otherClass is a pointer?
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
Const qualified member functions in most cases aren't allowed to change an object's members state. This means, that every member which is not mutable can not be modified in this function body. When dealing with pointers, you just say, that you won't modify a pointer value, not a pointee itself. It is because constness of a pointer is not propagated on its pointee.
In the upcoming standards it will be possible to change this behaviour by using propagate_const.
A simpler example to demonstrate the difference.
struct Foo { int m; };
const Foo f = {10};
f.m = 20; // Not allowed since modifying f.m modifies f.
struct Bar { int* p; };
int i = 10;
int j = 20;
const Bar b = {&i};
b.p = &j; // Not allowed since modifying b.p modifies b.
*(b.p) = j; // Allowed since it does not change b or b.p. It changes the value
// of what b.p points to.
When you put const on a method, all your data members are treated as being const. And that is why you get the error when you have OtherClass as a value, since it turns into a value of const OtherClass.
Now when you use a pointer of OtherClass you get const std::unique_ptr<OtherClass> and the const applies only to the pointer but not to the value it points to.
But why does this not fail then when m_otherClass is a pointer?
Because it is const correct to modify an object pointed by a const pointer, as long as it is a pointer to non-const such as in your case.
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
myMethod is declared const. Therefore this is a pointer to const. Therefore the lvalue this->m_otherClass is also const regardless of whether the object named by the lvalue is const or not.

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.

Returning const reference from a member get method

typedef vector<int> intvec;
class MyClass
{
intvec m_member;
public:
const intvec& GetVec();
};
const intvec& MyClass::GetVec()
{
return m_member;
}
int main(int argc, char *argv[])
{
MyClass myClassObj;
myClassObj.GetVec().push_back(11); //This doesn't work with const reference
myClassObj.GetVec().push_back(22); //This doesn't work with const reference
for(intvec::const_iterator iter = myClassObj.GetVec().begin(); iter != myClassObj.GetVec().end(); ++iter)
{
cout << *iter << ", " ;
}
cout << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
If the return type of GetVec() is non const reference then myClassObj.GetVec().push_back(11); works, but for const reference return this doesn't work (compilation error). I understand that if the GetVec() method itself was const i.e. const intvec& GetVec() const;, then myClassObj.GetVec().push_back(11); wouldn't work because that would mean modifying the "this" pointer.
You can overload the function on it's const qualification:
const intvec& GetVec() const; // Can access, but not modify, a const object
intvec& GetVec(); // Can access or modify a non-const object
Or you could just make the data member public and have exactly the same restrictions. There's little point in accessor functions unless they're used to restrict access or maintain class invariants, which these don't.
It is expected that a modifying function like push_back cannot be used on a const reference. Assuming that push_back is something that appends an item to an array, that constitutes a modification to the object, so if the object is constant then you can't do it.
I understand that if the GetVec() method itself was const i.e. const
intvec& GetVec() const;, then myClassObj.GetVec().push_back(11);
wouldn't work because that would mean modifying the "this" pointer
This is correct. And a getter function should have the const which prevents you from modifying "this".
const intvec& MyClass::GetVec() const { return m_member; }
A getter function is usually used to read data. Your class (the owner of m_member) should be the one to modify that member if needed. If you can modify the member from outside the class, one looses control of who by and when m_member is being changed (making debugging a difficult process and reducing the readability for developpers).
If you want to change m_member, you would typically add a method to your class to do so.
If the return type of GetVec() is non const reference then
myClassObj.GetVec().push_back(11); works, but for const reference
return this doesn't work (compilation error).
You simply cannot push an element on a const vector, because then it wouldn't be a const vector :). I think you are somewhat confused about the const keyword.
const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.

How to make const apply to C++ class members that are shared pointers

I'm struggling a bit trying to work out how to pass const pointers to classes around using shared pointers. Normally you just declare a pointer to an object const and you can no longer change any members in it, however if the class is using shared pointers the const does not seem to apply.
Here is a compilable example of the problem. In processA(), it should not be possible to add another element to the vector as the object is supposed to be const.
What am I doing wrong?
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
struct C {
int value;
};
struct B {
std::vector<C> vectorOfC;
};
struct A {
boost::shared_ptr<B> b;
};
typedef boost::shared_ptr<A> PtrA;
void processA(boost::shared_ptr<const A> a)
{
// This should not be possible because 'a' points to const
C c2;
c2.value = 456;
a->b->vectorOfC.push_back(c2);
for (std::vector<C>::const_iterator
i = a->b->vectorOfC.begin(); i != a->b->vectorOfC.end(); i++
) {
std::cout << i->value << std::endl;
}
}
int main(void)
{
C c;
c.value = 123;
PtrA a(new A);
a->b.reset(new B);
a->b->vectorOfC.push_back(c);
processA(a);
return 0;
}
All this stuff about shared_ptr is just a red herring.
You have pointer to a const object that has a pointer to a non-const object.
You can of course modify the latter object.
I think you're misunderstanding how const works with pointers.
const T * doesn't declare a const pointer, it declares a pointer to const. I.e. the pointer is mutable, but the thing(s) it points at are not.
When you add const to a pointer, you actually get T * const, which is a const pointer to mutable object(s), i.e. you can still change the pointee through the pointer.
In the case of std::shared_ptr (or boost::shared_ptr) adding const leaves you with const std::shared_ptr<T>, i.e. you cannot change the shared_ptr, but you can change the pointee, because the shared_ptr does not point to an immutable object.
To rectify this, you want to use std::shared_ptr<const T>, which is a mutable shared_ptr to a const object.
You can get around this by:
Making your boost::shared_ptrs pointers to const, or, if in certain instances it's acceptable to mutate the pointee
Using overloads of a getter (one overload being const, the other being mutable) to properly return boost::shared_ptr<const T> in the proper case, or, if callers don't actually need the boost::shared_ptr itself
Using overloads of a getter (one overload being const, the other being mutable) to return references to T, with the const overload returning const T & and the mutable overload returning T &

Can const member function return a non-const pointer to a data member?

Code goes first:
class A
{
public:
...
int *foo() const
{
return _px;
}
private:
int *_px;
}
The member function foo returns a non-const pointer to private member _px, which, I think, opens a door to modifying member _px, right?
Is foo a const member function? Should I add a const in front of the return type?
UPDATE
What a const-member-function should guarantee is that, it cannot change any data-member, right?
In my case, function foo doesn't open a door to modifying class As data-member _px, but a door to modifying what _px pointing to, So my question is, does this violate what a const-function should guarantee?
A const member function can only return a const pointer or reference to a member.
However, your example isn't returning a pointer to a member; it's returning a copy of a member that happens to be a pointer. That is allowed in a const member function (even if the pointer happens to point to another member).
This would not be allowed (note that it's now returning a reference):
int *& foo() const {return _px;}
but this would (returning a const reference):
int * const & foo() const {return _px;}
int *_px becomes int *const _px inside a const member function this implies that the pointer cannot be reseated but the data pointed to is still modifyable. Further your function returns a copy of the pointer so it does not matter anyways.
It does not open a door to modifying _px but rather what _px points to. It's up to you to decide whether you want to allow this or not.
For example, an iterator::operator-> would return a non-const pointer and const_iterator::operator-> would return a const pointer. Both methods can be const themselves.
Yes, for your case it can. However, it's generally advised to not to do this, because it allows changing constant objects:
void f(const A& a)
{
*(a.foo()) = 42; // damn!
}
Yes, for example see the std::streambuf pointers:
protected:
char* pbase() const;
char* pptr() const;
char* epptr() const;
http://en.cppreference.com/w/cpp/io/basic_streambuf/pptr