Reputation: 3416
Is it considered bad practice to mark a method as const even though the (pointer) data member it returns could be used "outside" the method to change its referenced object?
Take a look at the bone() method.
class SceneNode
{
public:
SceneNode(const char *nodeName, SceneNode *parent);
~SceneNode();
void setBone(Bone *bone);
Bone* bone() const { return _bone; }
};
private:
Bone *_bone;
};
I'm asking this because it's seems a little weird to me as the method can't be const if _bone wasn't a pointer, but an object (unless it returns a const reference or copy, of course).
Upvotes: 1
Views: 1120
Reputation:
Setting const
on the method results in well-defined behavior, but it may still be confusing behavior. Whether or not it's confusing depends on what the class is supposed to represent and what mental model a programmer is supposed to have.
For a similar example, consider std::vector
. This class also has a data member that is a pointer, and it has a method to retrieve that pointer; e.g. something like
template< typename T >
class vector
{
T *elements;
public:
T* data() { return elements; }
};
What about a const
version? Well, the thing that an instance of this class represents would be changed if you modified what the pointer refers to; i.e. if you change an element of the vector, then you've changed the vector. Consequently, a const version should not return a pointer to non-const:
template< typename T >
class vector
{
T *elements;
public:
T* data() { return elements; }
const T* data() const { return elements; }
};
On the other hand, consider std::shared_ptr
also has a data member that is a pointer, and has a method to retrieve that pointer:
template< typename T >
class shared_ptr
{
T *ptr;
public:
T* get() { return ptr; }
};
What if we get the pointer and change what it points to? Well, we haven't changed the shared_ptr
at all: it's still pointing to that object. So in this case, we should have
template< typename T >
class shared_ptr
{
T *ptr;
public:
T* get() const { return ptr; }
};
Upvotes: 2
Reputation: 36882
It depends
For example, unique_ptr::operator*()
is marked as const but doesn't add an additional const to the reference it returns. This makes sense because it's analogous to being able to modify what is pointed to by a T *const
but not change the pointer itself.
const std::unique_ptr<int> uptr(new int); // make_unique is better
*uptr = 1; // sure, I can modify what it points to
uptr.reset(); // no, this modifies the pointer itself (conceptually)
On the other hand, something like std::string
having char& operator[](size_t) const;
would be weird because the char
s inside of it should act like part of the object they are referenced by (the string
class)
const std::string s{"hello world"}; // I expect this to always be "hello world"
s[0] = 'a'; // error, you're conceptually modifying the string
It's up to you to decide what makes sense in your API and when your class is "morally const". If you had
void fun(const SceneNode& node);
int main() {
SceneNode n(...);
Bone b;
n.setBone(&b);
fun(n);
assert(*n.bone() == b); // is this a safe assumption?
}
If you think that assertion should always hold, then return a const Bone*
.
Upvotes: 4