在C++中有没有一种方法可以将一个类的函数限制在另一个类中调用(不使用继承,朋友)?

问题描述 投票:0回答:1

我想设计一个类,这个类的函数应该被限制为只能从另一个类调用。具体来说,在给定的代码中

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

一个用户(public user)可以创建一个类为 Club 并使用功能注册 registerMember() 因为它是公共的。我想让用户通过类的对象 Application,使用 addMemberToClub() 只用功能。如果用户走的是上述前一种方式,我就无法跟踪用户。有没有办法强制执行后一种方式?

  1. 我不想使用访问修饰符。protected 因为继承在这里没有意义。
  2. 我不想使用 friend 关键字,因为这被认为是不好的做法。
c++ oop api-design class-design access-modifiers
1个回答
2
投票

这里是一种 "锁和钥匙 "的方式,允许另一个类(而且只允许该类)甚至另一个类中的单个函数只访问一个成员函数,这一点与 friend 其中同时暴露了所有私有成员。

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

它还可以绕过 "哪个类先声明?"的问题,这个问题阻止了两个类相互友爱对方的单个成员函数,因为受限操作只需要跟随键类型的正向声明;另一个类型的完整定义可以(在本例中确实如此)出现在键定义之上,允许单个成员在键类型的友爱指令中被命名。

请注意,在这个解决方案中 "显而易见 "的事实是,同一类的其他成员可以访问锁定的函数。 不为真。 编译器防止 Locked::attacker() 不叫 Locked::secretive().

还请注意,我已经使用了 static 来减少我必须创建的对象数量,但是这种方法对于非静态成员函数也同样适用。


一个潜在的更简单的方法是用一个简单的标志来限制程序的哪些部分可以调用你的保护函数。

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

更容易理解, 但这是运行时的检查 而不是编译时的, 需要额外的工作来保证线程安全. 但它可以在不使用 friend 或继承。


1
投票

friend 是在这种情况下使用的合适机制。使 registerMember 私下里 ClubClub 可交 Application:

class Club
{
    // ...
    void registerMember(string memberName) { members.push_back(memberName); }
public:
    // ...
    friend class Application;
};

现在只有 Application 可谓 registerMemberClub 当然,也是。

这里有一个 演示.

© www.soinside.com 2019 - 2024. All rights reserved.