If a function takes a const & argument it should be immutable.
Why can I change values in a class passed as const?
How can I prevent a function taking const & to change values?
Note: compiles on vs2012 ad g++ 4.8.2
#include "iostream"
class Foo {
public:
Foo() : a(-99) {}
int a;
};
class Bar{
public:
Bar (Foo& f): rFoo(f), pFoo(&f), foo(f) {}
Foo& rFoo;
Foo* pFoo;
Foo foo;
};
void setA (const Bar & b){
Foo* f = new Foo();
Foo f2 = *f;
f->a = 7;
//b.foo.a = 8; // error C3490: 'a' cannot be modified because it is being accessed through a const object
//b.pFoo = f; // error C3490: 'pFoo' cannot be modified because it is being accessed through a const object
b.pFoo->a = 9; // OK... the pointer is const the location its pointing to not...
b.rFoo.a = 10; // Maybe ... the reference may be const, the location its is referencing not ...
b.rFoo = *f; // Why can I modify the reference ?
}
int main(int argc, char* argv[]){
Foo f;
Bar b(f);
std::cout << "one: " << b.rFoo.a << std::endl;
b.pFoo->a = 1;
b.foo.a = 2;
std::cout << "two: " << b.rFoo.a << std::endl;
setA(b);
std::cout << "three: " << b.rFoo.a << std::endl;
return 0;
}
Many Thanks in advance
You can modify the reference for the same reason you can modify the pointed to object by pFoo. Since you aren't modifying the objects member, but another referenced object.
b.rFoo = *f; // Foo's assignment operator invoked
That's why you should not expose members variables. Since you can't enforce a prevention to modify referenced objects, other than in your own member functions.
b.rFoo = *f
is not changing rFoo itself, it is equivalent to
b.rFoo.operator=(*f);
which copies *f to the object referenced by rFoo.
The const qualifier of an object indicates that the abstract state of this object won't change (i.e., from the client's point of view). This is not exactly the same as saying that the object's raw bits are not going to change.
It is forbitten to C++ compilers to consider objects as raw bits, unless they can resolve the problem of aliasing. which in your case the compiler cannot. This is due to the fact that Foo* pFoo is a pointer to an object and consequently the state of this object is modifiable. That is, even if the object that pFoo points to changes, the object that contains pFoo (i.e., b) doesn't change since pFoo continues to point to the same object.
Related
What is the meaning of const in declarations like these? The const confuses me.
class foobar
{
public:
operator int () const;
const char* foo() const;
};
When you add the const keyword to a method the this pointer will essentially become a pointer to const object, and you cannot therefore change any member data. (Unless you use mutable, more on that later).
The const keyword is part of the functions signature which means that you can implement two similar methods, one which is called when the object is const, and one that isn't.
#include <iostream>
class MyClass
{
private:
int counter;
public:
void Foo()
{
std::cout << "Foo" << std::endl;
}
void Foo() const
{
std::cout << "Foo const" << std::endl;
}
};
int main()
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
}
This will output
Foo
Foo const
In the non-const method you can change the instance members, which you cannot do in the const version. If you change the method declaration in the above example to the code below you will get some errors.
void Foo()
{
counter++; //this works
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++; //this will not compile
std::cout << "Foo const" << std::endl;
}
This is not completely true, because you can mark a member as mutable and a const method can then change it. It's mostly used for internal counters and stuff. The solution for that would be the below code.
#include <iostream>
class MyClass
{
private:
mutable int counter;
public:
MyClass() : counter(0) {}
void Foo()
{
counter++;
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++; // This works because counter is `mutable`
std::cout << "Foo const" << std::endl;
}
int GetInvocations() const
{
return counter;
}
};
int main(void)
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
std::cout << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
}
which would output
Foo
Foo const
Foo has been invoked 2 times
The const means that the method promises not to alter any members of the class. You'd be able to execute the object's members that are so marked, even if the object itself were marked const:
const foobar fb;
fb.foo();
would be legal.
See How many and which are the uses of “const” in C++? for more information.
The const qualifier means that the methods can be called on any value of foobar. The difference comes when you consider calling a non-const method on a const object. Consider if your foobar type had the following extra method declaration:
class foobar {
...
const char* bar();
}
The method bar() is non-const and can only be accessed from non-const values.
void func1(const foobar& fb1, foobar& fb2) {
const char* v1 = fb1.bar(); // won't compile
const char* v2 = fb2.bar(); // works
}
The idea behind const though is to mark methods which will not alter the internal state of the class. This is a powerful concept but is not actually enforceable in C++. It's more of a promise than a guarantee. And one that is often broken and easily broken.
foobar& fbNonConst = const_cast<foobar&>(fb1);
These const mean that compiler will Error if the method 'with const' changes internal data.
class A
{
public:
A():member_()
{
}
int hashGetter() const
{
state_ = 1;
return member_;
}
int goodGetter() const
{
return member_;
}
int getter() const
{
//member_ = 2; // error
return member_;
}
int badGetter()
{
return member_;
}
private:
mutable int state_;
int member_;
};
The test
int main()
{
const A a1;
a1.badGetter(); // doesn't work
a1.goodGetter(); // works
a1.hashGetter(); // works
A a2;
a2.badGetter(); // works
a2.goodGetter(); // works
a2.hashGetter(); // works
}
Read this for more information
Blair's answer is on the mark.
However note that there is a mutable qualifier which may be added to a class's data members. Any member so marked can be modified in a const method without violating the const contract.
You might want to use this (for example) if you want an object to remember how many times a particular method is called, whilst not affecting the "logical" constness of that method.
Meaning of a Const Member Function in C++ Common Knowledge: Essential Intermediate Programming gives a clear explanation:
The type of the this pointer in a non-const member function of a class
X is X * const. That is, it’s a constant pointer to a non-constant X
(see Const Pointers and Pointers to Const [7, 21]). Because the object
to which this refers is not const, it can be modified. The type of
this in a const member function of a class X is const X * const. That
is, it’s a constant pointer to a constant X. Because the object to
which this refers is const, it cannot be modified. That’s the
difference between const and non-const member functions.
So in your code:
class foobar
{
public:
operator int () const;
const char* foo() const;
};
You can think it as this:
class foobar
{
public:
operator int (const foobar * const this) const;
const char* foo(const foobar * const this) const;
};
I would like to add the following point.
You can also make it a const & and const &&
So,
struct s{
void val1() const {
// *this is const here. Hence this function cannot modify any member of *this
}
void val2() const & {
// *this is const& here
}
void val3() const && {
// The object calling this function should be const rvalue only.
}
void val4() && {
// The object calling this function should be rvalue reference only.
}
};
int main(){
s a;
a.val1(); //okay
a.val2(); //okay
// a.val3() not okay, a is not rvalue will be okay if called like
std::move(a).val3(); // okay, move makes it a rvalue
}
Feel free to improve the answer. I am no expert
when you use const in the method signature (like your said: const char* foo() const;) you are telling the compiler that memory pointed to by this can't be changed by this method (which is foo here).
Here const means that at that function any variable's value can not change
class Test{
private:
int a;
public:
void test()const{
a = 10;
}
};
And like this example, if you try to change the value of a variable in the test function you will get an error.
The const keyword used with the function declaration specifies that it is a const member function and it will not be able to change the data members of the object.
https://isocpp.org/wiki/faq/const-correctness#const-member-fns
What is a "const member function"?
A member function that inspects (rather than mutates) its object.
A const member function is indicated by a const suffix just after the member function’s parameter list. Member functions with a const suffix are called “const member functions” or “inspectors.” Member functions without a const suffix are called “non-const member functions” or “mutators.”
class Fred {
public:
void inspect() const; // This member promises NOT to change *this
void mutate(); // This member function might change *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
changeable.inspect(); // Okay: doesn't change a changeable object
changeable.mutate(); // Okay: changes a changeable object
unchangeable.inspect(); // Okay: doesn't change an unchangeable object
unchangeable.mutate(); // ERROR: attempt to change unchangeable object
}
The attempt to call unchangeable.mutate() is an error caught at compile time. There is no runtime space or speed penalty for const, and you don’t need to write test-cases to check it at runtime.
The trailing const on inspect() member function should be used to mean the method won’t change the object’s abstract (client-visible) state. That is slightly different from saying the method won’t change the “raw bits” of the object’s struct. C++ compilers aren’t allowed to take the “bitwise” interpretation unless they can solve the aliasing problem, which normally can’t be solved (i.e., a non-const alias could exist which could modify the state of the object). Another (important) insight from this aliasing issue: pointing at an object with a pointer-to-const doesn’t guarantee that the object won’t change; it merely promises that the object won’t change via that pointer.
In const objects only const methods can be called. All fielnd in such a method considered as const field.
Last issue has curious effect:
pointer becomes a const pointer int* const, which is not the same as a pointer to const const int*. Thus you can alter the object which pointer points to, but can't make pointer to point to another object.
reference should become a const reference, but it is invariantly a const reference: you can not re-init it to another object. But again you can alter the object which reference refers to.
I'm not sure to understand why I can modify an object from a const method, look:
#include <iostream>
struct Foo {
int a = 0;
void change() {
a = 3;
}
};
struct Test {
Foo* f;
Test(): f{new Foo} {}
void test() const {
f->change();
}
};
int main()
{
Test t;
std::cout << "before: " << t.f->a << "\n";
t.test();
std::cout << "after: " << t.f->a << "\n";
}
Not only it compiles but it prints:
0
3
So I was able to modify the logical state of an object from a const method. Is that because I used a pointer?
The const applies to the pointer itself, f, not to what this pointer is pointing to. The type of f inside your const-qualified member function, Test::test(), is Foo* const (i.e., const pointer to Foo), not const Foo* (i.e., pointer to const Foo). That's why you can modify what the pointer is pointing to in spite of the const qualification of the member function.
Note that the following sample member function, Test::test2(), does fail to compile since it is const-qualified and tries to modify the pointer data member, f:
void Test::test2() const {
f = nullptr; // <-- error
}
No, you did not modify the logical state of an object:
f->change();
f, the object's class member, is as it's always been, here. Its value hasn't changed. It's not pointing to some other object now. It's still pointing to the same object it's always been pointing to.
What you did modify is the object to which f points to. Which is a different object.
Yes, you modified the logical state of the object. That's allowed, because const prohibits modifying the physical state.
The object t has one data member, f, whose type is "pointer to Foo". When the t object is created, it's f member points at an object of type Foo. After the call t.test() the f member still holds the same address, so it points at the same object of type Foo as it did before.
If test() had tried to change the value stored in f (i.e., the pointer) the const would have prevented it.
void Test::test() const { f = new Foo; } // error: `f` is const in this context
I have a function getA() which returns a const reference of base type A, since it's const, it cannot dynamic_cast it, so I make a copy of the const reference and then created a reference to the copied object, but when I call dynamic_cast to the reference of the copied object, it fails, the code is shown below:
struct A {
int c = -1;
virtual ~A() {}
};
struct B : A {int aa = 0;};
const A& getA(){
std::unique_ptr<A> ap(new B);
return *ap;
}
int main()
{
const A& a = getA();
A acopy = a;
acopy.c = -2;
A& acopyr = acopy;
std::cout << a.c << std::endl;
try{
B& b = dynamic_cast<B&>(acopyr);
std::cout << b.aa << std::endl;
}catch(std::bad_cast b){
std::cout << "bad" << std::endl;
}
}
The output is
-1
bad
acopy is an object of dynamic (and static) type A. Notice how it was declared: an object of type A. So of course it cannot be cast to a B&.
From your description, I take it you just want to dynamically cast getA() to a const reference to B. There's nothing stopping you from that:
const B& b = dynamic_cast<const B&>(getA());
Side note: I assume the getA implementation in your question is just for demonstration purposes, but it's very wrong. As soon as ap goes out of scope (that is, as soon as getA returns), it will destroy the object to which it points, so you're returning a dangling reference and thus invoking Undefined Behaviour.
I am reading C++ primer. I encountered the following code:
#include <iostream>
#include <string>
using namespace std;
class PrintString {
public:
PrintString(ostream &o = cout, char c = ' '): os(o), sep(c) {}
void operator() (const string &s) const { os << s << sep; }
private:
ostream &os;
char sep;
};
int main() {
const PrintString printer;
printer("ABC");
return 0;
}
This code works, but I don't know why. Below is what I think, it would be great if anyone could help to point out where I am wrong...
Here, 'printer' is a const PrintString object, so its data members are const, so 'printer.os' is a const reference to cout. Therefore, we should not be able to write to 'printer.os' since writing to cout changes it.
Thanks in advance!
The reference isn't being modified, only what it refers to. This works the same with a pointer. If you had a pointer to int data member (int*), using it in a const member function would make its type int* const. You aren't able to change the pointer itself, but you can change what it points to. For example:
struct Foo
{
int a;
int* p = &a;
void foo() const
{
p = new int; // ERROR! Not allowed to modify const pointer
*p = 100; // OK, it's a pointer to a non-const int
}
};
So when using os, you're only modifying the object that it refers to, not the reference itself.
Your confusion with const-ness is better explained with a pointer rather than a reference.
Say you have:
struct A {int data;};
struct B
{
B(A* ptr) : aPtr(ptr) {}
A* aPtr;
};
int main()
{
A a1;
A a2;
const B b(&a1);
// This makes b a const object.
// This makes b.aPtr a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
b.aPtr = &a2; // Not ok.
b.aPtr->data = 10; // OK.
}
The const-ness of b.aPtr is analogous to what you would see had you used a raw pointer.
int main()
{
A a1;
A a2;
A* const aPtr1 = &a1;
// This makes aPtr1 a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
aPtr1 = &a2; // Not ok.
aPtr1->data = 10; // OK.
A const* aPtr2 = &a1;
// This makes aPtr2 a pointer to a const object. That means, you can change where it points to
// but you cannot change the value of what it points to.
aPtr2 = &a2; // ok.
aPtr2->data = 10; // Not ok.
}
When it comes to references, it is similar but with a small twist. There is no such thing as a non-const reference. A reference, once initialized, cannot reference another object.
A a1;
A& ref = a1; // ref cannot be changed to reference any other object.
In your case,
const PrintString printer;
has no effect on the const-ness of the member variable PrintString::os. It continues to reference a non-const ostream. This allows you to use:
const PrintString printer;
printer("ABC");
Try to think of the reference member as a const pointer member variable, and how constness would propagate to member variables when you declare a const object of such class. What gets const is the pointer itself, not what it points to. The same semantics happen with references, The thing is that references are already const (like a const pointer variable), so it doesn't change nothing for them.
#include <iostream>
struct S {
int *p;
int *const cp;
int &ref;
};
int main() {
using namespace std;
int i = 10;
const S s{&i, &i, i};
// s.p = &i; // can't do this, s.p gets const
*s.p = 20; // changing the pointee
cout << i << endl;
// s.p = &i; // can't do this, s.p was const already, and would get if it weren't
*s.cp = 30; // changing the pointee
cout << i << endl;
// s.ref ?; // can't make a reference refer to other object
s.ref = 40; // changing the pointee
cout << i << endl;
}
I know this has been asked a lot, but the only answers I could find was when the const-ness was actually casted away using (int*) or similar. Why isn't the const qualifier working on pointer type member variables on const objects when no cast is involved?
#include <iostream>
class bar {
public:
void doit() { std::cout << " bar::doit() non-const\n"; }
void doit() const { std::cout << " bar::doit() const\n"; }
};
class foo {
bar* mybar1;
bar mybar2;
public:
foo() : mybar1(new bar) {}
void doit() const {
std::cout << "foo::doit() const\n";
std::cout << " calling mybar1->doit()\n";
mybar1->doit(); // This calls bar::doit() instead of bar::doit() const
std::cout << " calling mybar2.doit()\n";
mybar2.doit(); // This calls bar::doit() const correctly
}
// ... (proper copying elided for brevity)
};
int main(void)
{
const foo foobar; // NOTE: foobar is const
foobar.doit();
}
The code above yields the following output (tested in gcc 4.5.2 and vc100):
foo::doit() const
calling mybar1->doit()
bar::doit() non-const <-- Why ?
calling mybar2.doit()
bar::doit() const
When a foo instance is const, its data members are const too, but this applies differently for pointers than you might at first think:
struct A {
int *p;
};
A const obj;
The type of obj.p is int * const, not int const *; that is, a constant pointer to int, not a pointer to constant int.
For another way to look at it, let's start with a function:
template<class T>
T const& const_(T const &x) {
return x;
}
Now imagine we have an A instance, and we make it const. You can imagine that as applying const_ on each data member.
A nc;
// nc.p has type int*.
typedef int *T; // T is the type of nc.p.
T const &p_when_nc_is_const = const_(nc.p);
// "T const" is "int * const".
const T &be_wary_of_where_you_place_const = const_(nc.p);
// "const T" is "int * const".
// "const T" is *not* "const int *".
The variable be_wary_of_where_you_place_const shows that "adding const" is not the same as prepending "const" to the literal text of a type.
I am going to answer my own question in this case. Fred Nurk's answer is correct but does not really explain the "why". mybar1 and *mybar1 are different. The first refer to the actual pointer and the latter the object. The pointer is const (as mandated by the const-ness on foo; you can't do mybar1 = 0), but not the pointed to object, as that would require me to declare it const bar* mybar1. The declaration bar* mybar1 is equivalent to bar* const mybar1 when the foo object is const (i.e. pointer is const, not pointed to object).
C++ by default gives a so called bitwise constness - meaning it ensures that no single bit of object has been changed, so it just checks the address of the pointer.
You can read more about it in great book "Effective c++" by S. Meyers