5

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);
    }
};
  • You probably have it private for a reason. However, if you need to access mbar, you can create a getMar() function inside class Bar which returns mbar. – stomo21 Apr 27 '14 at 07:18
  • If possible (but it depends effective classes) I'd derive a new class from Object and I'd return that one. It'll be a private implementation detail class (with a reference to original object) and it'll restrict access to what you want (performing any validation you may need). Methods will still be visible but _real_ object will be accessible only through a _local_ controlled entry point. – Adriano Repetti Apr 27 '14 at 07:19
  • If you control `Bar` and can modify it at will, why not call `Foo` from a member function of `Bar`? – n. m. could be an AI Apr 27 '14 at 07:19
  • @stomo21: Wouldn't getMbar() allow callers to freely modify the resource that mbar points to? – SingularByte Apr 27 '14 at 07:30
  • @n.m.: I'm using Bar as a wrapper so I'd prefer not to put many functions in it. – SingularByte Apr 27 '14 at 07:30
  • @Adriano: I'll try it out and see if it works. – SingularByte Apr 27 '14 at 07:30
  • Yes, because it is a pointer to an object in memory. If you don't want them to have access, you need to create a copy then pass a pointer to the copy instead. – stomo21 Apr 27 '14 at 07:33
  • @Adriano, I've looked into deriving a new class, but Object is part of the library so it's going to be tricky to do so. – SingularByte Apr 27 '14 at 07:55
  • Yes it may be (well it depends if you can override all non-const member functions). If not even to pass a copy (as suggested by stomo21) may be viable (but it may be even more tricky if that objects exposes many other references). – Adriano Repetti Apr 27 '14 at 07:57
  • @stomo21: Creating copies is just going to be too expensive. Object contains pixel data, and there's going to be a lot of Objects in the proper code. – SingularByte Apr 27 '14 at 07:58
  • "I'm using Bar as a wrapper" --- You have a class with encapsulated, private data. **The** way to work with encapsulated data is via member functions (and, when member functions are not convenient, via friends --- members and friends **are the same thing** for almost every purpose). If you don't want that, don't encapsulate your data in the first place. You can't have it both ways. – n. m. could be an AI Apr 27 '14 at 08:42
  • I just realised I was doing something dumb. If I move Bar b; inside Foobar (as a private member) and make mbar public, that should stop any encapsulation problems, right? The only thing that would gain public access to it by doing that would be Foobar, which is exactly what I wanted all along. – SingularByte Apr 27 '14 at 09:04

2 Answers2

3

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.

Community
  • 1
  • 1
NPH
  • 103
  • 4
2

If the member is private, it's probably private for a reason...

If Bar has to be the only owner of Obj, then it should not expose it, as any other change to Obj might cause Bar to act incorrectly. Although, if Bar does not have to be the only owner of Obj, you can either put a getter use dependency injection and pass it into Bar from outside, this way you can later pass it to foo as well.

A solution i think you should avoid is putting a call to foo inside Bar. This might violate the Single Responsibility Principle

I bealive that in this case tough, you can use a friend method. I will refer you to a FAQ claiming that friend is not allways bad for encapsulation.

No! If they're used properly, they enhance encapsulation.

You often need to split a class in half when the two halves will have different numbers of instances or different lifetimes. In these cases, the two halves usually need direct access to each other (the two halves used to be in the same class, so you haven't increased the amount of code that needs direct access to a data structure; you've simply reshuffled the code into two classes instead of one). The safest way to implement this is to make the two halves friends of each other.

If you use friends like just described, you'll keep private things private. People who don't understand this often make naive efforts to avoid using friendship in situations like the above, and often they actually destroy encapsulation. They either use public data (grotesque!), or they make the data accessible between the halves via public get() and set() member functions. Having a public get() and set() member function for a private datum is OK only when the private datum "makes sense" from outside the class (from a user's perspective). In many cases, these get()/set() member functions are almost as bad as public data: they hide (only) the name of the private datum, but they don't hide the existence of the private datum.

Similarly, if you use friend functions as a syntactic variant of a class's public access functions, they don't violate encapsulation any more than a member function violates encapsulation. In other words, a class's friends don't violate the encapsulation barrier: along with the class's member functions, they are the encapsulation barrier.

(Many people think of a friend function as something outside the class. Instead, try thinking of a friend function as part of the class's public interface. A friend function in the class declaration doesn't violate encapsulation any more than a public member function violates encapsulation: both have exactly the same authority with respect to accessing the class's non-public parts.)

Community
  • 1
  • 1
user1708860
  • 1,683
  • 13
  • 32
  • I agree friends aren't _always_ a bad thing but IMO they couple classes pretty tight (and I'd avoid that in this case). In general they're a tool (again IMO) that _may_ be used if other solutions aren't viable (or too expansive compared to benefits) but this is not the case. Here proxy pattern may work pretty well (and implementation may evolve even transforming proxy into bridge). – Adriano Repetti Apr 27 '14 at 07:45
  • I'll have to read up a bit on patterns before I try a proxy or bridge pattern, and if that fails, I'll try friend functions. – SingularByte Apr 27 '14 at 07:52