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;
}