Making a public member function private to certain classes

166 Views Asked by At

Is there a way to make a public member function private to certain classes? The reason I need to do this is because some classes must use something else in place of that public member function, else there will be serious bugs, but it is easy to forget and do that anyway. Example:

class Location {
    std::list<Person*> everyonePresent;
public:
    const std::list<Person*>& getEveryonePresent() const {return everyonePresent;}
};

class DoSomething {
    Person& person;
    std::list<Person*> everyoneVisible;
public:
    DoSomething(Person& p) : person(p), everyoneVisible(p.getLocation().getEveryonePresent()) {
       // everyoneVisible now removes those who are hiding.
   }
   void execute() {
       // ....
       ApproachSomeone(person, everyoneVisible).execute();
       // ...
   }
};

Now ApproachSomeone::execute() may accidentally use person.getLocation().getEveryonePresent() when it is not supposed to. But Location::getEveryonePresent() needs to be public because it is used in so many places, but ApproachSomeone is one of the few classes that must never use it. ApproachSomeone is a class because it is used in many other places (command pattern).

1

There are 1 best solutions below

1
Pepijn Kramer On

There is a way to model access on a per function basis if you are willing to do some dependency injection, or willing to do an explicit cast of a class to an abstract base class to gain access to private member functions.

I have used these approaches in cases where a class would need more then one "friend", but those friends should have distinct access to various member functions. It is still easier to do the correct thing, but not impossible to get "private" access. It is a good compromise and code can be checked on who gets access to what (so it is possible to detect misuse in code reviews too).

Live demo here: https://onlinegdb.com/TFVGm-HVW

First, we make two interfaces for per-class access to a class A:

#include <iostream>

class interface_for_class_b
{
public:
    virtual void method_for_class_b() = 0;
};

class interface_for_class_c
{
public:
    virtual void method_for_class_c() = 0;
};

Then, make a class A which inherits from both, but keeps the implementation private. This hides the member functions from everyone.

class A :
    public interface_for_class_b,
    public interface_for_class_c
{
public:
    void some_public_method()
    {
        std::cout << "A::some_public_method()\n";
    }

private:
    void method_for_class_b() override 
    {
        std::cout << "A::method_for_class_b()\n";
    };

    void method_for_class_c() override 
    {
        std::cout << "A::method_for_class_c()\n";
    };
};

Then, use dependency injection:

  • class B receives interface_for_class_b
  • class C receives interface_for_class_c
class B
{
public:
    explicit B(interface_for_class_b& a) :
        m_interface_to_a{ &a } { }

    void some_method()
    {
        std::cout << "B::some_method()\n";
        // B can access "private" function through the explicit interface.
        m_interface_to_a->method_for_class_b();
    }

private:
    interface_for_class_b* m_interface_to_a;
};

class C
{
public:
    explicit C(interface_for_class_c& c) :
        m_interface_to_a{ &c } { }

    void some_method()
    {
        std::cout << "C::some_method();\n";
        m_interface_to_a->method_for_class_c();
    }
    
private:
    interface_for_class_c* m_interface_to_a;
};

Here is how we use these classes:

int main()
{
    A a;
    B b{ a }; // inject dependency to give b access to "private"functions
    C c{ a };

    a.some_public_method();
    // a.method_for_class_b();
    // is not accessible, it is private
    b.some_method();
    c.some_method();

    // if you don't want to or cannot do dependency injection
    // there is an alternative way to get access to the specific methods
    // this explicit step says you want detailed access to class a
    // so it is also self-documenting.
    interface_for_class_b& itf{ a };
    itf.method_for_class_b();

    return 0;
}