Search code examples
c++pointersmember-variables

How can I use a private member variable in a non-member function, when the variable happens to be a pointer?


Essentially my problem is that a function in a library I'm using, (function Foo in this code), requires a pointer to an object (Object* mbar) as a parameter. However, mbar is a private member variable to bar.

Normally, I'd just use a getter and pass by value, but if I pass the pointer, that would give direct access to the resource, which would break encapsulation. Any code could just call the getter and get free reign to modify it.

The next thing I thought was that I could use const pointers because they disallow modifying the resourse they point to, but as far as I could tell, I'd need to modify Foo to accept it, which is impossible as it's a library function.

The final thing I can think of is simply using a friend of Bar to call FoobarFunction, but I've always been told that friend functions are a last resort.

Is there a way to do this without breaking encapsulation in some way?

//Main.cpp

#include "Foobar.h"

int main()
{
    Foobar fb;
    Bar b;
    fb.FoobarFunction(b);
    return 0;
}

//Bar.h

#include "Object.h"

class Bar
{
private:
    Object* mbar;
};

//Foobar.h

#include "Foo.h"
#include "Bar.h"

class Foobar
{
public:
    void FoobarFunction(Bar bar)
    {
        Foo(bar.mbar);
    }
};

Solution

  • The Easy Way Out

    You can make the pointer const and then cast it when you pass it to the library function

    Foo(const_cast<Object *>(bar.mbar));
    

    This will work if Foo does not try to modify mbar. The cast removes the constness "in name only." Attempting to modify a secretly-const value can lead to Terrible Things.

    But Really...

    Even if there was a way to make Bar return a "read-only" pointer, the code sample in your question would still violate encapsulation. This particular flavor of non-encapsulation is called feature envy: the data lives in one object, but another object is doing most of the data manipulation. A more object-oriented approach would be to move the manipulation and the data into the same object.

    Obviously, the sample code you've given us is much less complicated than your actual project, so I can't know the most sensible way to restructure your code. Here are a couple of suggestions:

    1. Move the FoobarFunction into Bar:

      class Bar
      {
      private:
          Object* mbar;
      public:
          void FoobarFunction()
          {
              Foo(mbar);
          }
      };
      
    2. Use dependency injection. Initialize mbar before creating Bar, then pass mbar into Bar's constructor.

      int main()
      {
          Object *mbar;
          Foobar fb;
          Bar b(mbar);
          fb.FoobarFunction(mbar);
          return 0;
      }
      

      In this example, Bar is no longer the "owner" of mbar. The main method creates mbar directly and then passes it to whoever needs it.

      At first glance, this example appears to break the guideline I mentioned earlier (the data and behavior are stored in different objects). However, there is a big difference between the above and creating a getter on Bar. If Bar has a getMBar() method, then anybody in the world can come along and grab mbar and use it for whatever evil purposes they wish. But in the above example, the owner of mbar (main) has complete control over when to give its data to another object/function.

    Most object-oriented languages besides C++ don't have a "friend" construct. Based on my own experience, dependency injection is a better way of solving many of the problems that friends were designed to solve.