Search code examples
c++pointersreferenceencapsulation

Safe/Correct use of pointers


I understand that you shouldn't return a pointer or reference to member data because it breaks encapsulation. But I'm not sure if that is what's happening in this case and I could use some clarification...

Tree header-

#include "TreeNode.h"
class Tree
{
public:
    explicit Tree(Tree* treeToCopy);
    explicit Tree(TreeNode*);
    TreeNode& getRootNode() const;
private:
    TreeNode* rootNodeOfExpressionTree;
};

Tree.cpp-

#include "Tree.h"


Tree::Tree(Tree* treeToCopy)
{
    rootNodeOfExpressionTree = &treeToCopy->getRootNode();
}
Tree::Tree(TreeNode* rootNodeOfExpressionTree)
    :rootNodeOfExpressionTree(rootNodeOfExpressionTree)
{
}//empty constructor body

TreeNode& Tree::getRootNode() const
{
    return *rootNodeOfExpressionTree;
}

My question is with regard to the getRootNode() method. Since rootNodeOfExpressionTree is a pointer, and I'm dereferencing it and returning a reference to whatever it was pointing to, then I'm not breaking encapsulation correct?


Solution

  • There is no problem with breaking encapsulation. None whatsoever. The problem is with breaking invariant. Encapsulation is a myth. Invariant is a real thing.

    Here a caller to getRootNode get's access to the data Tree member points to. What that means is that the caller can't change the pointer itself, but it can change the data the pointer points to. I realize this might be confusing, so here is a simple example:

    struct A {
        A() : member(new int(42)) {}
        int& get() const { return *member; }
    
        private:
        int* member;
    };
    
    ....
    A a; // *a.member is 42
    int& x = a.get();
    x = 56; // *a.member is 56!
    

    The question is, is this data an invariant worth protecting? I have no idea, only you can tell.