What does const really mean? Read-only seems to encapsulate its meaning for me, but, I'm not sure I'm right.
If read-only and const are different, could someone tell me why?
What prompted this question was this answer where he states const "just" means read-only in C. I thought that's all const meant, regardless of whether it was C or C++. What does he mean?
For an answer to the specific differences in const in C vs C++, I've created a new question: How does "const" differ in C and C++? as per R..'s suggestion.
By declaring a variable as const you indicate compiler that you have no intentions of modifying that variable. But it does not mean others don't have! It's just to allow some optimization and to be notified by a compile error (note, that it's mostly compile error, while const == ReadOnly would mean runtime errors).
const does not mean read only, because you can write const volatile, that would mean it could change by itself anytime, but I have no intentions to modify it.
EDIT: here is a classical example: consider I'm writing the code that reads current time from a memory-mapped port. Consider that RTC is mapped to memory DWORD 0x1234.
const volatile DWORD* now = (DWORD*)0x1234;
It's const because it's a read-only port, and it's volatile because each time I will read it it will change.
Also note that many architectures effectively make global variables declared as const read-only because it's UB to modify them. In these cases UB will manifest itself as a runtime-error. In other cases it would be a real UB :)
Here is a good reading: http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
The compiler won't allow something declared as const to be modified. It is as you say.
It's mostly used in function prototypes to inform the user that a function won't touch this or that when passed pointers. It also works as kind of failsafe for yourself.
A lot of people are telling you that const means you can't modify it. That is patently false. const can trivially be cast away. Note this snippet:
void foo(const int *somevalue)
{
int *p = (int*) somevalue;
*p = 256; // OMG I AM EVIL!!!!11
}
Your compiler will not stop you from doing this. So, what then is the purpose of const? I'd call it more of a suggestion. It reminds you as you look at function prototypes of the contract that your functions expect. Your compiler will yell at you if you carelessly break it. (But not if you intentionally break it, as with the above cast.)
In some cases the standard intentionally breaks const. Note the return values of strstr for example: by definition it will return some offset into the const buffer you provide it... But the returned value is not const. Why? Well, this would break meaningfully using the return value of strstr on a non-const buffer.
Two byte for byte identical (except for the comments) minimal case examples...
First in C, gcc will emit a warning...
/* Function taking a pointer to an array of
two read only integers.*/
void a( const int (* parray)[2]);
void b(void)
{
int array[2] = {1,2};
const int crray[2] = {1,2};
/* C reserves the right to stash this in a read-only location.*/
a( &array);
/* warning: passing argument 1 of ‘a’ from incompatible pointer type*/
a( &crray); /* OK!*/
}
Now the same thing in C++... g++ is quite happy with it.
// Function taking a pointer to an array
// of two integers which it promises not to modify.
// (Unless we cast away it's constness ;-P)
void a( const int (* parray)[2]);
void b(void)
{
int array[2] = {1,2};
const int crray[2] = {1,2};
a( &array); // C++ has no problem with this.
a( &crray); // OK!
}
const char * hello_1{ "Hello!" };
const char hello_2[]{ "Hello!" };
char * ptr{};
// take away the const-nes
// ptr = (char *)hello_1;
// *ptr = '*'; <-- write access violation
// hello_1 is in a read only memory
// take away the const-nes
ptr = (char *)hello_2;
*ptr = '*'; // <-- OK
// hello_2 is modifiable
Pointers point to memory, and char * points to memory in the data segment which is read only. The difference between char * and char [] is that while both are declared the same way on the data segment, char [] is treated as readable because it is pushed onto the stack if it is used.
C++ allows for the definition of const member functions. const member functions are the only functions that be called on const objects. Also, const member functions cannot modify any data members of a class (unless the data member marked mutable).
class Foo
{
int data;
void Bar();
void ConstBar() const;
};
void Foo::ConstBar() const
{
// Error! cannot modify i as ConstBar is a const member function.
// i = 0;
}
// Usage:
const Foo foo_instance;
// Error! cannot call non-const member on a const object.
// foo_instance.Bar();
// OK
foo_instance.ConstBar();
Const means that a pointer or reference cannot be used for a write or read-modify-write operation without casting away const. It does NOT mean what the C++ standard tries to claim it means (the C++ standard is just wrong on this).
A variable defined like this:
/* auto */ int const x = 1;
is patently NOT read-only since otherwise it could not be initialised. Rather, the kind of variable x is "reference const to int" (and NOT reference to const int) or alternatively lvalue const of int. Note carefully the "const" is associated with a pointer or reference it has nothing to do with the storage, nor the type of the value residing in that storage.
This is rather unfortunate because the contract provided by const is extremely weak, and in particular fails to allow caching of a pointed at or referred to memory location, precisely because it does NOT mean immutable storage.
The bottom line is: const is an access modifier associated with a symbolic reference or pointer which is used by the programmer to allow the symbol provider to establish an obligation on the symbol client, or for the symbol client to promise the symbol provider it does not modify storage via this symbol (for example a function accepting a pointer const to int promises not to modify the pointed at int).
This has nothing to do with variables:
int const *p = (int*)malloc(sizeof(int));
and clearly little to do with storage (malloc'ed store is always writable).
Instead you should think of const as a way of communicating invariants, obligations or requirements between parts of the program, put in place by the programmer for the programmers purposes, and propagated by the type system. Unfortunately the type system isn't sound and fails to properly propagate constness correctly:
X *last;
struct X { int a; X() : a(0) { last=this; } };
X const x; // x is const?
last->a = 1; //really ??
IMHO the only opportunity a compiler has to make store immutable is for actual constants such as string literals (maybe) or static (global) storage. Automatic, heap, and temporary storage cannot be made read-only in practice.
Related
I am using a function from an external library (here represented by the printer-function).
By chance, I noticed, that I can pass a class member from a const member function like done by the print()-function. Even if I compile using
g++ -Wall -Wextra -Wpedantic -Werror -o test test.cpp
I don't obtain an error.
This seems to be horribly wrong, because effectively I am modifying the object by calling a const member function. Why is the compiler not complaining?
Here is the code:
#include <iostream>
using namespace std;
void printer(void* data) {
static_cast<char*>(data)[0] = '1';
cout << static_cast<char*>(data) << endl;
}
void setUp(char* data, int length) {
for(int i=0; i<length; i++)
data[i] = '0';
data[length] = '\0';
}
class Test {
private:
char* data;
public:
Test(int length) {
data = new char(length+1);
setUp(this->data, length);
}
~Test() {
delete data;
}
void print() const {
printer(this->data);
}
};
int main() {
Test T(3);
T.print();
return 0;
}
My best guess is, that the compiler only guarantees, that the pointer variable is not modified, i.e. not pointing to a different physical address after the function call. But I also noticed, that the compiler throws errors when I try something similar with a std::vector<char> instead of char* and a modified print-function:
void print() const {
printer(this->myVector.data());
}
So if my guess is correct, why is it not possible to do something similar with the pointer obtained by data() of a STL vector?
You have a char* data member.
In the context of a const-qualified method, that means the member may not be mutated.
The member is the pointer. Changing a pointer means changing where it points.
Just to be clear, the effective qualified type inside the method is
char * const
and not
const char *
Which is all to confirm that you're absolutely right that
My best guess is, that the compiler only guarantees, that the pointer variable is not modified, i.e. not pointing to a different ... address after the function call.
However, for the vector, you want to call data on something that again has effective qualified type std::vector<char> const& ... so you need the const-qualified overload of std::vector::data, and that returns const char*. This is what you expected in the first place, but it's not the same behaviour as the raw pointer.
The reason for the difference is that the vector was written to express ownership of that data, so a const vector should have const contents. Raw pointers may own the data they point at, but there's no way to express this, and no way to have different const behaviour for owning and non-owning raw pointers. This is one of the may reasons to avoid raw pointers where possible.
This is a common way of allowing a const method to change data. The immutable thing in Test is the pointer address data; which is not altered by print. You are nonetheless free to mutate what the pointer is addressing.
Another way round a const method is to declare a member as being mutable. Arguably, ensuring an internal string attribute is e.g. null terminated for printing could be considered a legitimate use of mutable.
See this piece from Kate Gregory at CppCon 2017 where she provides an interesting example (a method is designed to be const but a cache is added later):
https://youtu.be/XkDEzfpdcSg?t=854
Also see this question for a lively discussion on the topic:
Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?
Apparently, a const member function is still allowed to change data that the class member are pointing to. Here's an example of what I mean:
class MyClass
{
public:
MyClass();
int getSomething() const;
private:
int* data;
};
// ... data = new int[10];, or whatever
int MyClass::getSomething() const
{
data[4] = 3; // this is allowed, even those the function is const
return data[4];
}
I'd prefer if this was not allowed. How should I define "data" so that "getSomething() const" isn't allowed to change it? (but so that non-const functions are allowed to change it.) Is there some kind of "best practice" for this? Perhaps std::vector?
In a const member function, the type of data changes from int* to int *const:
int * const data;
which means, it's the pointer which is const in the const member function, not the data itself the pointer points to. So you cannot do the following:
data = new int[100]; //error
as it's attempting to change the pointer itself which is const, hence disallowed, but the following is allowed:
data[0] = 100; //ok
Because changing the content doesn't change the pointer. data points to same memory location.
If you use std::vector<int>, then you can achieve what you want. In fact, vector solves this problem, along with the memory management issues, therefor use it:
class MyClass
{
public:
MyClass();
int getSomething() const;
private:
std::vector<int> data;
};
MyClass::MyClass() : data(10) {} //vector of size 10
int MyClass::getSomething() const
{
data[4] = 3; // compilation error - this is what you wanted.
return data[4];
}
Avoid non-RAII design as much as you can. RAII is superior solution to memory management issues. Here, with it, you achieve what you want. Read this:
Resource Acquisition Is Initialization (RAII)
There is no way to do what you want. Your const member function can't change the value of data (the address it points to), but there is nothing about not changing the contents it points to as you have already noticed. Using a std::vector would help, since within a const member function you would actually have a const vector, not being able to call any of its mutable functions.
The reason you should use std::vector is actually because you want to store a collection of ints, and not an int pointer, so why not use it?
It would also solve your const'ness issue.
Here's how it works: declaring a method const will make all class members const. In your case, that means that in the method scope, your member data will become a constant pointer to an int. That means you can change the int (also meaning array members) as long as data points to the same location. Using std::vector, data would become a const std::vector, on which you could only call const functions. So yes, you should use std::vector.
After reading two dozen similar questions, I still have not found a solution to the underlying problem. I can easily fix the problem, but I would really like to understand the problem.
We are talking about C++.
Usage of char* is a constraint due to libraries that supply the input.
I have a public static member method of factory F:
static A *create(const char *&s);
When I call this from another class B like this:
char *s = data;
A a = F::create(s);
I get an error due to 'invalid arguments'
When I change the variable declaration to
const char *s = data;
Everything is fine...
AFAIK, you can provide a non-const variable to a const parameter, because const parameters are a guarantee not a requirement. This does not seem to be valid with references.
Create consumes characters, hence the pointer cannot be const; s is supplied to similar functions immediately afterwards. The array should not be edited, hence I would like to have a const array. Reference to a pointer seems exactly like what I want.
I am assuming some internal language specs forbidding my initial solution, but I would really like to understand why!
You can't do that because it's unsafe - it would let you change a non-const pointer so it actually points to a const object.
Consider what could happen if it were allowed:
void foo(const char* & s)
{
s = "Pops"; // This is valid.
}
int main()
{
char t[] = "Unicorns";
char* p = t;
foo(p);
p[0] = 'O'; // Oops, modifying a constant object - undefined.
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why isn't the const qualifier working on pointer members on const objects?
Consider the following class that has a pointer member int *a. The const method constMod is allowed by the compiler even though it modifies the pointer data. Why doesn't the compiler make the pointer data const in the context of the const method? If a was just an int we wouldn't be allowed to modify it in a const method.
class ConstTest
{
public:
ConstTest(int *p): a(p) {}
void constMod() const {
++(*a);
}
int *a;
};
I'm using g++ on linux.
Inside constMod(), the declaration of a is treated as:
int *const a;
which means the pointer has a constant value, not what it points to. It sounds like you are expecting it to be treated as:
const int *a;
which is different.
The pointer itself is not modified, only the data pointed. Even if it sounds strange from a human point of view, from a compiler point of view, none of the member of the class are altered.
It's just an ownership issue... there's no way the compiler can know if the pointed-to object is logically part of the object or not, so it's left for the programmer to police such issues. const members are allowed to perform operations with side effects, as long as they don't modify their own apparent value. It's not really any different from letting them call say std::cout::operator<<() or some other non-const function....
What is const in the above case is the pointer itself. You are not allowed to do ++a;. However, it doesn't prevent you from modifying the data being pointed to.
I believe because here you are trying to change the value where datamember is pointing to.
If you try to modify the data member it would be error.
Meaning, you'll get error if you make a point to something else instead of changing the value
const member functions will not allow you to modify it's members.
In your example, you have a pointer that points an int.
And in the const method, you are modifying the value that it is pointed to but not the pointer itself.
Try giving, ++a, which will actually modify the pointer value and will not be allowed in your const method.
Consider
const T immutable_object0;
const T immutable_object1;
const T* mutable_view = ...a condition... ? &immutable_object0 : &immutable_object1;
// then later you reseat
mutable_view = ...another condition... ? &immutable_object0 : &immutable_object1;
or
const int immutable_data[120]
const int* mutable_view = immutable_data;
for(const int* end = immutable_data + 120; mutable_view != end; ++mutable_view) {
// can't modify *mutable_view
}
It's because pointers don't always have ownership. In some circumstances pointers are views to an object (first example) or are iterators into a raw array (second example). For those cases it doesn't make sense to restrict the operations available on the pointers just because the data pointed to is immutable (or seen as immutable).
The first example is a bit contrived but a valid version of it is when you use a pointer member to implement object association and you don't want the hassle of a reference member. Sometimes the pointed-to types happen to be const.
Would it be possible for a public function to return a pointer to a private variable in the class. If so / if not, what would happen? would it crash or is there anything highly unsafe about this? Can the pointed data be read or written to?
Thanks
Yes, a member function may return a pointer (or reference) to a private data member. There is nothing wrong with this except that in most circumstances it breaks encapsulation.
The data member can certainly be read via the returned pointer or reference. Whether or not it can be written to depends on whether the returned pointer or reference is to a const-qualified object (i.e., if you return a const T*, you won't be able to modify the pointed-to T). For example:
class Example
{
public:
int* get() { return &i; }
const int* get_const() const { return &i; }
private:
int i;
};
int main()
{
Example e;
int* p = e.get();
int a = *p; // yes, we can read the data via the pointer
*p = 42; // yes, we can modify the data via the pointer
const int* cp = e.get_const();
int b = *cp; // yes, we can read the data via the pointer
*cp = 42; // error: pointer is to a const int
}
Yes it can be returned and it can be read and written to. It is no more or less dangerous than taking the address of any other variable. Public/private/protected are syntactical constructs that are checked at compile time, and they aren't "contagious" or part of the type of something.
A private member is no different from a public member, or any other type of instance. So, yes, you can take pointers to them, and return them.
When taking pointers to any instance-based member, you have to be careful that the parent class is not deleted and doesn't go out of scope, unless you take that pointer and make a true copy of the data/object it points to. If it is deleted or goes out of scope, the pointer becomes a dangling pointer, and you can't use it anymore without your app exploding (or working on non-existent objects, and thus making your program do crazy unexpected things, but not crashing).
Design considerations:
Exposing any of your internal implementation details is potentially a violation of encapsulation. However, if you only want to encapsulate HOW the object you're returning is created/retrieved, then this is a reasonable solution. This would allow you to change the class to get the member object some other way (like querying a file, or an internal dictionary) without breaking code that calls these methods.
"would it crash or is there anything highly unsafe about this? Can the pointed data be read or written to?"
private/protected/public have no effect whatsoever at runtime, so they can't influence the program execution or even crash the program. They are checked at compile time and just cause your compiler to throw an error while compiling if there is a violation.
The const qualifier won't protect you on those cases. Considering James's response, please try to cast the const int * to int *
int* cp = (int*)e.get_const();
You will still be able to modify.