Const class object with a pointer member - c++

let's have an example program:
struct Example
{
int* pointer;
Example(int* p) : pointer(p) {}
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer = &var; // error
*object.pointer = var;
}
I do know why the error appears - when creating a const Example object, pointer is actually const pointer, not a pointer to const. Therefore, when assigning &var to object.pointer it is not correct but assigning var to the *object.pointer works perfectly fine.
So is there any way to make this line:
*object.pointer = var;
not compile? I want the value that pointer points to to also be const. In other words, when creating a const object:
const Example object(&var);
I want constness to be applied also on the value itself, not only on the pointer:
const int* const pointer; // something like that

Either declare the data member like
const int *pointer;
or make it a protected or private data member. In the last case you can declare a non-constant member function that allows to change the value pointed to by the pointer. For a constant object of the class the function will not be called. For example
struct Example
{
private:
int* pointer;
public:
Example(int* p) : pointer(p) {}
int & get_value() { return *pointer; }
const int & get_value() const { return *pointer; }
};

const Example object(&var);
This creates a const object. Once a const object is created it cannot be changed. This is what a constant object means, by definition.
object.pointer = &var;
This attempts to change this object's member. This object cannot be changed. It is a constant object now, hence the error.
I want constness to be applied also on the value itself, not only on
the pointer:
const int* const pointer; // something like that
"Something like that" declares a pointer that's both a constant value itself, and it points to a constant value.
If you want an object with a pointer to const int, that would be:
const int *pointer;
And making the object itself constant has absolutely nothing to do with it, whatsoever. It only prevents modification of the object itself. Once you make something const it cannot be changed for any reason.

You can do something like that by
Making pointer an member function that return the pointer
Having non-const version of the function return a reference to the pointer to allow assignment to that
Having const version of the function return a copy of the pointer to prevent assignment
struct Example
{
private:
int* pointer_internal;
public:
Example(int* p) : pointer_internal(p) {}
int*& pointer() { return pointer_internal; }
int* pointer() const { return pointer_internal; }
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer() = &var; // error: lvalue required as left operand of assignment
*object.pointer() = var;
}

Related

why I can edit value inside pointer while the passed object is const to ref? [duplicate]

This question already has answers here:
Why is it allowed in C++ to modify a constant object's pointer member variable's memory from outside?
(4 answers)
Closed last year.
I declared this member function to take const reference to object.. So, I am expecting that when I try to edit anything in the object there will be an error.
But in the following code I tried to edit value in a member pointer and it works !!
m_ptr is a member pointer
void Test::takeObj(const Test& obj)
{
*obj.m_ptr = 3;
}
The const property is not transitive, when dereferencing pointers. You access the pointer m_ptras read-only, but this pointer itself is not a pointer to const. You write to the object pointed to by m_ptr, not m_ptr itself.
You could write two getter functions instead of a public access to the m_ptr member variable. One getter function is const and returns a pointer to const, one is not const and returns a normal pointer.
class Test {
private:
int* m_ptr;
public:
Test(int* ptr) : m_ptr(ptr) {};
const int* ptr() const { return m_ptr; }; // this overload is called for const Test object (const this*)
int* ptr() { return m_ptr; }; // this overload is called for non-const Test object
};
int main()
{
int a;
Test x(&a); // x is non-const
const Test& x2 = x; // x2 is const
int* p = x.ptr(); // okay
*p = 3; // okay
int* p2 = x2.ptr(); // error: invalid conversion from 'const int*' to 'int*'
*p2 = 4;
}
Then inside the const takeObj you can only access the const int* version of m_ptr.
Bonus info:
On C the test object (if it only contained an int*) would be a int**. The const parameter variant would had to be a const int* const*.

Returning pointer on class member from const member function

Are these definitions of pointer() equal?
class Example
{
public:
AnotherClass* const pointer() const;
AnotherClass* pointer() const;
private:
AnotherClass* m_pointer;
}
Does it guarantee me what somebody can't change m_pointer memory pointed to?
You need the return type for the first method to be one of these:
AnotherClass const * pointer() const;
or
const AnotherClass* pointer() const;
What you have done is return a const pointer to a non-const object.
What you want is a non-const pointer to a const object.
Also, to follow the typical practice your second method should not be const.
AnotherClass* pointer();
The two overloads for pointer only differ in the return type. Your C++ compiler will not accept that, this is an error.
Assuming that pointer() just returns m_pointer:
The declaration AnotherClass* pointer() const; would expose the value of m_pointer. Users of the class would not be able to change m_pointer, but they could change the object that m_pointer points to.
The declaration AnotherClass* const pointer() const; is quite similar to AnotherClass* pointer() const; but the value returned (the temporary pointer) is const. This declaration does not really make sense because you already can't change the return value in the first declaration. You can't write example.pointer() = nullptr in either cases. And when you assign the return value to a variable like in p = example.pointer() it makes no difference if the return value of pointer() is const or not.
The declarations const AnotherClass* pointer() const; and AnotherClass const * pointer() const; would expose the value of m_pointer. Users of the class would not be able to change m_pointer, and they would not be able to change the object that m_pointer points to.
There are a few things to consider here:
What is const: the pointer or the pointee?
Let's see the differences between these different declarations.
int x, y;
int * p = &x; // 0
const int * p = &x; // 1
int const * p = &x; // 2
int * const p = &x; // 3
const int * const p = &x; // 4
int const * const p = &x; // 5
(0) means that p can change (p = &y; is OK) and what it points to (x) can be changed via p (*p = y; is OK).
(1) and (2) are equivalent and mean that p can change (p = &y; is OK) and what it points to (x) cannot be changed via p (*p = y; is an error).
(3) means that p cannot change (p = &y; is an error) and what it points to (x) can be changed via p (*p = y; is OK)
(4) and (5) are equivalent and mean that p cannot change (p = &y; is an error) and what it points to (x) cannot be changed via p (*p = y; is an error).
An easy way to remember this is looking at the * and think that it separates the objects constness, that is,
(a) a const before * (e.g. const int * [...] or int const * [...]) means the pointed object (of type int) is const; and
(b) a const after * (e.g. [...] * const p [...];) means that the pointer is const and cannot be changed.
What happens if a function returns a const object?
Consider:
class some_class;
const some_class a(); // equivalent to 'some_class const a();'
someclass b();
const int c(); // equivalent to 'int const c();'
int d();
Then, (assuming some_class has an accessible assignment operator) we have:
some_class x;
a() = x; // illegal : the returned object is const
b() = x; // OK : the returned object is not const
It's natural to believe that c() and d() behave the same but this is not the case:
c() = 0; // illegal
d() = 0; // also illegal!!!
The reason is that if a function returns a fundamental type (e.g. int, bool, char, any pointer type, ...) then the returned object cannot be assigned to. Therefore,
returning a const object of fundamental type behaves the same as to returning a non-const one.
What about overloading on the return type?
Consider
int f(); // first overload.
double f(); // second overload.
This is illegal. We cannot overload on the return type only. One reason is that we can always ignore the object returned by a function. For instance, if overloading on the return type was allowed, then which overload of f would be called below?
int main() {
f(); // which f?
return 0;
}
Answering the question...
Consider the first declaration
AnotherClass* const pointer() const;
From what we have seen, this means that pointer() returns a const object of type AnotherClass* (a pointer type). Since a pointer type is a fundamental type, the function pointer above behaves the same as if it was declared as
AnotherClass* pointer() const;
which is the second overload. This is the first reason why having these two overloads doesn't make sense. But this is just a weak reason compared to the one that follows.
When I say "behaves the same" I don't mean "equivalent". The return type of the two overloads are different. As we have seen, we cannot overload on the return type only. This is illegal and the code doesn't compile.
Does it guarantee me what somebody can't change m_pointer memory pointed to?
No, again, both overloads behave the same and return a pointer to an object that can be changed via this pointer. What you want is this:
const AnotherClass* pointer() const { return m_pointer; }
// or
// AnotherClass const* pointer() const { return m_pointer; }
Notice the const at the left of *.

Returning non-const reference from a const member function

Why does returning the reference to a pointed-to member variable work, but not the other? I know that a const member function should only return const references, but why does that not seem true for pointers?
class MyClass
{
private:
int * a;
int b;
public:
MyClass() { a = new int; }
~MyClass() { delete a; }
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
};
int main(void)
{
MyClass m;
m.geta() = 5; //works????
m.getb() = 7; //doesn't compile
return 0;
}
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.
Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):
m.geta() = 5; //works, as the data is modifiable
Difference between : const int* and int * const.
const int* means the pointer is non-const, but the data the pointer points to is const.
int * const means the pointer is const, but the data the pointer points to is non-const.
Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:
const int & getb(void) const { return b; }
Now it compiles fine!.
Because a becomes int * const a;. That is, you cannot change the value of a (change what it points at), just as const says. The const-ness of what a points at is a completely different issue.
Please see my answer here for a thorough discussion of const and const member functions.
Nawaz's answer is very good. However, the point about the compiler catching the error need not always hold. The C++ FAQ warns about it here.
The good news is that the compiler will often catch you if you get this wrong. In particular, if you accidentally return a member of your this object by non-const reference [...] the compiler will often detect it and give you a compile-time error [...].
The bad news is that the compiler won’t always catch you: there are some cases where the compiler simply won’t ever give you a compile-time error message.
Translation: you need to think. If that scares you, find another line of work; “think” is not a four-letter word.

Returning const 'this' pointer

if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue expression whose value is the address
of the object for which the function is called. The type of this in a member function of a class X is X*. If the member
function is declared const, the type of this is const X*, if the member function is declared volatile, the type of
this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile
X*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};

const correctness and return values - C++

Please consider the following code.
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t is a const variable and value() is a constant member-function which returns const T&. AFAIK, a const type is not assignable to a non-const type. But how foo* f = t.value(); compiles well. How this is happening and how can I ensure value() can be only assigned to const foo*?
Edit
I found that, this is happening on when templates are used. Following code works as expected.
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
Why the problem is happening when templates are used?
Because you have two levels of indirection - in your main function, that call to value returns a reference to a const pointer to a non-const foo.
This can safely be copied into non-const pointer to a non-const foo.
If you'd instantiated test with const foo *, it would be a different story.
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
Update
From the comment:
value() returns const T& which can
only be assigned to another const
type. But in this case, compiler is
safely allowing the conversion.
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
const int c = 5;
int n = c;
Here, I had some const data in c, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c has not been modified.
Now, suppose your foo had some data in it:
struct foo { int n; };
If I have a non-const pointer to one of those, I can modify the n value through the pointer. You asked your test template to store a pointer to a non-const foo, and then made a const instance of test. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test, so it cannot be made to point to another object. However, the object it points to can have its contents modified.
Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo * into each place where there's a T.
const T& value() const
Notice that you have a reference to a const T there. So the return value will be a reference to something const: a foo *. It's only the pointer address that can't be modified. The object it points to can have its contents modified.
In your second example, you got rid of the reference part, which changes the meaning and makes the const modifier apply to the object that the pointer points to, instead of applying to the pointer itself.
Use the following template specialization:
template<typename T>
class test<T*>
{
public:
test() {}
const T* value() const
{
return f;
}
private:
T* f;
};
After including this, g++ says:
d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’
There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your main function you try to change the address pointed to by the f member of t you'll see that you can't: encapsulation is perfectly preserved.
This is the same principle that makes the following code valid:
void foo(std::vector<int *> const & v)
{
*v[0] = 0; // op. [] returns const & to int *
}
People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.
The only solution is to do as Amit says and provide a specialization of your class for T*.