views:

756

answers:

8

I need to implement read-only access to a private member container. If I return a constant reference is it possible to const_cast it and obtain a full access to the member? What's the technique to be used?

Thanks.

+3  A: 
const int &ref = your_object.your_function();
*(int*)&ref = 1234;
Maurice Perry
I believe the const_cast operator is the preferred way to do the unpreferred action.
Dave Van den Eynde
Jeez people! Why the downvotes? All Maurice has done is demonstrate that a regular C-style cast is enough to "break" the constness -- do you really think the evil perpetrators Jack is worried about care about style issues?
j_random_hacker
Agree @j_random_hacker. Will upvote to compensate.
David Rodríguez - dribeas
@j_random, @dribeas: thanks
Maurice Perry
+14  A: 

Is it safe to return a const reference to a private member

Yes as long as the lifetime of the reference does not exceed the lifetime of the object which returned it. If you must expose the private member you do not want modified, this is a good way to do so. It's not foolproof but it's one of the better ways to do so in C++

Is it possible to use const_cast to actually mess around with member

Yes and there is nothing you can do to prevent this. There is no way to prevent someone from casting away const in C++ at any time. It's a limitation / feature of C++.

In general though, you should flag every use of const_cast as a bug unless it contains a sufficiently detailed comment as to why it's necessary.

JaredPar
Lifetime in general is not a problem. If you didn't return a reference, you'd return by value, i.e. a temporary. That has its own lifetime issues, which are quite similar.
MSalters
The lifetime problems you get with references are quite different from the ones you get with temporaries. A reference can become invalid without warning. With a temporary, you know when it goes out of scope -- if you read the standard *really* closely! ;-)
Pontus Gagge
i had to do with a class that had its Get methods non-const. it sucked a lot. what i did was still to return ptr-to-const and cast the const away to call the get functions, and commented "that crappy class should make its GetFoo method const to make us stop cast const away"
Johannes Schaub - litb
Absolutely agree with your sentiment about const_cast.
caspin
+1  A: 

Yes, so it's probably not what you want to do. On the other hand, if someone is going to the trouble of const casting your reference, it's possible they really know what they are doing.

dicroce
+4  A: 

*const_cast* can be definitely used to obtain the full access to the member. I guess you can not stop people if they are hell bent on shooting themself on the foot. If the private member is not heavy, consider returning a copy of that variable.

Naveen
I was going to say the exact same thing. If you think people will abuse a const ref, return a copy by value. However, a better solution is to give anybody who abuses const_cast in this way a sound kicking instead. It's a recipe for future disaster.
markh44
+1  A: 

It is possible to obtain a full access. But what for?

Don't forget to make accessor to be const correct

const MyType& getMyValue() const;

Also you can inject you private value in the callback.

void doJob( callback c )
{
    c( myPrivateValue_ );
}
Mykola Golubyev
+9  A: 

Returning a const & is a sensible thing to do in many circumstances, particularly if the object being returned is large or cannot be copied.

Regarding the const_cast, remember the "private" access specifier in C++ is there as an aid to the programmer - it is not intended to be a security measure. If someone wants access to an object's private members, it can get them, no matter what you try to do to prevent it.

anon
The problem with the second paragraph is that, while I can't prevent people from subverting encapsulation and perhaps breaking class invariants, I really don't want to go around encouraging it.
David Thornley
I agree - I didn't mean to suggest people should not use "const"
anon
+2  A: 

Don't worry about users doing const_casts just to break your invariants. If they really want to break your code they can without you providing accessors to your internal attributes. By returning a constant reference, the common user will not mistakenly modify your data.

Encapsulation prevents mistakes, not espionage A malicious coder can break it anyway if they really care and know the environment (compiler). Const-ness is lost in the compilation process (in all compilers I know of). Once the compilation unit is converted into binary objects, those objects do not know about const-ness, and that can be exploited to take advantage.

// a.h
class A
{
public:
   A( int a ) : data_( a ) {}
   A get() const { return data_; }
private:
   int data_;
};

// malicious.h
class A;
void change( A& a, int new_value );

// malicious.cpp
//     does not include a.h, but redefines an almost exact copy of it
class A {
public:
   A( int a ) : data_( a ) {}
   int get() const { return data_; }
   int data_; // private removed
};
void change( A& a, int new_value )
{
   a.data_ = new_value;
}

// main.cpp
#include "a.h"
#include "malicious.h"
int main()
{
   A a(0);
   change( a, 10 );
   std::cout << a.get() << std::endl; // 10
}

While the code above is incorrect (One definition rule is broken, there are two definitions for class A), the fact is that with most compilers the definition of A and malitious A are binary compatible. The code will compile and link, and the result is that external code has access to your private attributes.

Now that you know of it, _**don't do it**_. It will later be a maintenance pain in the ***. That has cost Microsoft quite a bit of money in providing backwards compatibility to software that used private parts of the API returned objects (new versions of the API that shared the same public interface but changed the internals would break some third party application code). With some broadly available software the provider (Microsoft in this case) will go through the pain of providing backwards compatibility, but with lesser known applications they won't and suddenly your previously running application will fail in all sort of ways.

David Rodríguez - dribeas
+2  A: 

I think it was Herb Sutter that once said that one should "Protect against Murphy, not against Machiavelli." That is, you should do everything possible to protect against the code being used incorrectly by accident, but there's nothing you can do about people abusing your code on purpose.

If someone really wants to break your code, they can, even if it's by #define private public before including your header (and thus creating an ODR violation, but I digress).

So yes, passing back a const ref is fine.

Kaz Dragon