McLovin
McLovin

Reputation: 3416

C++ const member function returns a pointer

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

Answers (2)

user1084944
user1084944

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

Ryan Haining
Ryan Haining

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 chars 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

Related Questions