Вы можете объявить свои данные отдельно
struct Data
{
/* member variables */
};
Иметь класс интерфейса, способный манипулировать указанными данными, будут ли все члены защищены
class Interface
{
protected:
Interface(Data &data) : m_data{data} {}
void bar() { /* implementation */ }
void baz() { /* implementation */ }
Data &m_data;
};
Получены классы, которые делают общедоступными определенные элементы
class A : private Interface
{
public:
A(Data &data) : Interface{data} {}
using Interface::bar;
};
class B : private Interface
{
public:
B(Data &data) : Interface{data} {}
using Interface::baz;
};
Таким образом, вы также можете иметь пользователей, которые могут иметь перекрывающийся доступ к некоторым функциям, не выполняя их несколько раз.
class Admin : private Interface
{
public:
Admin(Data &data) : Interface{data} {}
using Interface::bar;
using Interface::baz;
};
Конечно, в зависимости от того, как вы используетеданные, вам может понадобиться указатель или общий указатель, возможно, добавить некоторую синхронизацию между обращениями из нескольких потоков.
Пример кода с использованием этой модели:
void test()
{
Data d{};
auto a = A{d};
a.bar();
// a.baz is protected so illegal to call here
auto b = B{d};
b.baz();
// b.bar is protected so illegal to call here
auto admin = Admin{d};
admin.bar();
admin.baz();
}
Это кажется мне эффективным в том смысле,что у вас есть только один набор данных и одна реализация для манипулирования данными, независимо от того, сколько у вас типов пользователей.