Weird const-ness troubles

J

James Aguilar

Take the following code example:

class Array {
double *m_array;
public:
Array() { m_array = new double[10]; }
double *begin() const {return m_array;}
};

int main() {
const Array a = Array();
double *iShouldNotExist(a.begin());
iShouldNotExist[0] = -1000;
return 0;
}

This code compiles and runs on g++ 3.3.3 with -pedantic and -ansi set. As I
see it, this code allows me to take an object declared as const and mess
around with its private internals because of a method call that promises
that the object will not be changed. What's up with that? I know that the
right way is to return a const double *, but shouldn't this kind of thing be
disallowed?

- JFA1
 
L

Lionel B

James Aguilar said:
Take the following code example:

class Array {
double *m_array;
public:
Array() { m_array = new double[10]; }
double *begin() const {return m_array;}
};

int main() {
const Array a = Array();
double *iShouldNotExist(a.begin());
iShouldNotExist[0] = -1000;
return 0;
}

This code compiles and runs on g++ 3.3.3 with -pedantic and -ansi set. As I
see it, this code allows me to take an object declared as const and mess
around with its private internals because of a method call that promises
that the object will not be changed.

Note that:

i) Array::begin (correctly) does not change the "internals" of an Array
object and

ii) iShouldNotExist[0] = -1000;

does not change the value of a.m_array (it can't, since a is declared
const). It does, of course, change the value of the memory location
pointed to by a.m_array. I'm not sure that this qualifies as "messing
around with the private internals" of object a.
What's up with that? I know that the
right way is to return a const double *,
indeed

but shouldn't this kind of thing be
disallowed?

Probably not...

Regards,
 
M

Matthias Kaeppler

James said:
Take the following code example:

class Array {
double *m_array;
public:
Array() { m_array = new double[10]; }
double *begin() const {return m_array;}
};

int main() {
const Array a = Array();

What kind of weird initialisation is that? :)
const Array a;
would be sufficient.
double *iShouldNotExist(a.begin());
iShouldNotExist[0] = -1000;
return 0;
}

This code compiles and runs on g++ 3.3.3 with -pedantic and -ansi set.

Yes, the code is, at least syntactically, correct.
All fields of 'a' are const, that is, the pointer. That means, you can't
change the address it's holding, but you still can change the content of
the memory it's pointing to.
As I see it, this code allows me to take an object declared as const and mess
around with its private internals because of a method call that promises
that the object will not be changed. What's up with that? I know that the
right way is to return a const double *, but shouldn't this kind of thing be
disallowed?

You sort of dig your own hole. You declare a method as const, which
allows the client to mess with the Array internals. That kind of doesn't
make sense. Either declare it non-const, so it may not be invoked on a
const Array anymore, or just take it out, if you don't want the client
to have access to the array internals. It's your responsibility, and
only yours, that the client can't do that if he's not supposed to.

Solution: Don't write methods which return references to internal data,
if you don't want the client to mess with them. It's like blaming the
thief for robbing the bank after you handed him the keys.
 
R

Rolf Magnus

James said:
Take the following code example:

class Array {
double *m_array;
public:
Array() { m_array = new double[10]; }
double *begin() const {return m_array;}
};

int main() {
const Array a = Array();
double *iShouldNotExist(a.begin());
iShouldNotExist[0] = -1000;
return 0;
}

This code compiles and runs on g++ 3.3.3 with -pedantic and -ansi set. As
I see it, this code allows me to take an object declared as const and mess
around with its private internals because of a method call that promises
that the object will not be changed.

The "private internals" that you refer to are a pointer to double, and that
pointer cannot be modified, since it is returned by value, i.e. a copy of
it is returned.
What's up with that? I know that the right way is to return a const
double *, but shouldn't this kind of thing be disallowed?

How does the compiler know what you want? In this case, the object pointed
to is created by the Array object, which probably means that you consider
the Array to own that object. But you could also have something like:


class Object
{
public:
Object(Object* parent = =)
: parent_(parent)
{}

Object* parent() const { return parent_; }

void non_const_function() { /* ... */ }

private:
Object* parent_;
};

int main()
{
Object a;
const Object b(&a);
b.parent()->non_const_function();
}

Now b doesn't really own a, so why should modification of it be disallowed?
 
J

James Aguilar

Matthias Kaeppler said:
Solution: Don't write methods which return references to internal data, if
you don't want the client to mess with them. It's like blaming the thief
for robbing the bank after you handed him the keys.

I know the solution, but it seemed to me, based on my former understanding
of things, that it should be enforced. Now I see that that is not the case.

- JFA1
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,430
Messages
2,571,676
Members
48,796
Latest member
Greg L.

Latest Threads

Top