Reputation: 51
I want to design a class having a function which should be restricted to be called from another class only. Specifically, in the given code
class Club
{
int id;
string name;
vector<string> members;
int generateId()
{
static int i=1;
return i++;
}
public:
Club(string name) { this->name = name; this->id = generateId(); }
void registerMember(string memberName) { members.push_back(memberName); }
int getId() { return id; }
};
class Application
{
vector<Club> clubs;
public:
void registerClub(Club &club) { clubs.push_back(club); }
void addMemberToClub(int clubId, string memberName)
{
for(Club club: clubs)
{
if(clubId == club.getId())
club.registerMember(memberName);
}
}
};
An user(public user) can create an object of the class Club
and register using the function registerMember()
since it's public. I want the user to register via an object of the class Application
, using the addMemberToClub()
function only. If the user goes by the former way mentioned, I can't keep track of the user. Is there a way to enforce the latter?
protected
since inheritance has no meaning here.friend
keyword, since it's considered bad practice.Upvotes: 1
Views: 1289
Reputation: 283733
Here is a "lock-and-key" way to permit another class (and only that class) or even a single function in another class to access just one member function, unlike friend
which exposes all private members at the same time:
#include <iostream>
class Key;
class Locked
{
static const char* const Greeting;
public:
static Key secretive();
static void attacker();
};
struct Admin
{
void doit();
};
class Key
{
~Key() = default;
//friend class Admin;
friend void Admin::doit();
friend Key Locked::secretive();
};
void Admin::doit()
{
Locked::secretive();
std::cout << Locked::Greeting; // compile error
}
constexpr const char* Locked::Greeting = "Hello!\n";
Key Locked::secretive()
{
std::cout << Greeting;
return Key();
}
void Locked::attacker()
{
std::cout << Locked::Greeting; // ok, it's just private
Locked::secretive(); // compile error, it's locked down tight
}
int main()
{
Admin a;
a.doit();
std::cout << Locked::Greeting; // compile error
Locked::secretive(); // compile error
}
It also works around the "which class is declared first?" problem that prevents two classes from mutually friending individual member functions of each other, because the restricted operation needs to follow only a forward declaration of the key type; the full definition of the other type can (and in this example does) appear above the key definition, allowing individual members to be named in the key type's friend directive.
Note that in this solution the "obvious" fact that other members of the same class can access the locked function is NOT true. The compiler prevents Locked::attacker()
from calling Locked::secretive()
.
Note also that I've used static
in this example to minimize the number of objects I had to create, but the approach works just fine for non-static member functions too.
A potentially MUCH easier way to restrict what part of the program can call your protected function is with a simple flag:
class Application
{
static bool addingMember = 0;
public:
static bool isRegistrationOk() { return addingMember; }
void registerClub(Club &club) { clubs.push_back(club); }
void addMemberToClub(int clubId, string memberName)
{
addingMember = true;
for(Club club: clubs)
{
if(clubId == club.getId())
club.registerMember(memberName);
}
addingMember = false;
}
};
void Club::registerMember(string memberName)
{
assert(Application::isRegistrationOk());
members.push_back(memberName);
}
Much easier to grok, but it's a runtime check not compile-time, and requires additional work to be made thread-safe. But it accomplishes the goal with no usage of friend
or inheritance.
Upvotes: 2
Reputation: 60238
friend
is an appropriate mechanism to use in this case. Make registerMember
private in Club
, and Club
can grant friendship to Application
:
class Club
{
// ...
void registerMember(string memberName) { members.push_back(memberName); }
public:
// ...
friend class Application;
};
Now only Application
can call registerMember
, and Club
as well, of course.
Here's a demo.
Upvotes: 1