You should generally avoid returning references because it makes it crazy-hard to understand when constructors/destructors get called. However, returning a reference can be faster.
Yes, you should use const whenever possible. It makes a contract that your code will not change something. Remember, a non-const variable can be passed in to a function that accepts a const parameter. You can always add const, but not take it away (not without a const cast which is a really bad idea).
No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .
The grammar doesn't allow you to declare a “const reference” because a reference is inherently const . Once you bind a reference to refer to an object, you cannot bind it to refer to a different object.
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++
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.
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.
const int &ref = your_object.your_function();
*(int*)&ref = 1234;
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 ) {}
int 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With