C++ 中有没有办法将给定类的函数限制为仅另一个类(不使用继承,朋友)?

Posted

技术标签:

【中文标题】C++ 中有没有办法将给定类的函数限制为仅另一个类(不使用继承,朋友)?【英文标题】:Is there a way in C++ to restrict a function of a given class to another class only(without using inheritance, friend)? 【发布时间】:2020-08-30 00:45:39 【问题描述】:

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

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

用户(公共用户)可以创建类Club 的对象并使用函数registerMember() 进行注册,因为它是公共的。我希望用户通过 Application 类的对象注册,仅使用 addMemberToClub() 函数。如果用户使用前面提到的方式,我无法跟踪用户。有没有办法强制执行后者?

    我不想使用访问修饰符protected,因为继承在这里没有意义。 我不想使用 friend 关键字,因为它被认为是不好的做法。

【问题讨论】:

friend 在这里似乎很合适。那有什么问题?我不知道有一条说永远不要使用friend @cigien 试图保持这种语言的独立性。 @AbhishekGhosh:你的标题说“在 C++ 中”,所以它与语言无关 我不确定你的意思。你最终必须用一些语言来实现它,不同的语言有不同的机制来实现类似的效果。以及@BenVoigt 所说的:) friend 的替代方法是添加一个只有预期调用者知道如何构造的数据类型的虚拟参数。这可用于仅启用对单个成员函数的访问,而不是像 friend 那样一次性授予对所有私有成员的访问权限。 【参考方案1】:

这是一种“锁定和密钥”方式,允许另一个类(并且只允许该类)甚至另一个类中的单个函数访问一个成员函数,这与 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

它也适用于“首先声明哪个类?”阻止两个类相互友好的单个成员函数的问题,因为受限操作只需要遵循键类型的前向声明;其他类型的完整定义可以(并且在本例中)出现在键定义上方,允许在键类型的朋友指令中命名各个成员。

请注意,在此解决方案中,"obvious" fact that other members of the same class can access the locked function 不正确。编译器阻止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 或继承的情况下实现了目标。

【讨论】:

我花了整整 5 分钟的时间盯着代码(我懒得运行它并检查编译器说什么:))来弄清楚为什么会这样,我现在看到了: Key 有一个私有析构函数,它与特定的目标函数secretive 有“朋友关系”,而后一个函数返回可丢弃的未使用的Key 对象。它是可丢弃且未使用的,但即使要对其进行优化,正式仍需要销毁它,而现在 attacker 无法做到这一点,因为在此范围内无法访问 ~dtor。 OTOH,doit 可以,因为它也是好友。好东西!好技巧! @quetzalcoatl:您的分析完全正确。 Admin::doit()(以及来自secretive() 本身的递归调用)是唯一可以调用Locked::secretive() 的函数。最初我什至没有朋友secretive(),但虽然编译器没有抱怨,但从非 void-returning 函数的末尾脱落是未定义的行为,所以我必须添加 return 语句,然后需要访问析构函数也是如此。【参考方案2】:

friend 是在这种情况下使用的适当机制。在Club 中将registerMember 设为私有,Club 可以将友谊授予Application

class Club

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

现在只有Application 可以调用registerMember,当然Club 也是如此。

这是demo。

【讨论】:

"现在只有 Application 可以调用 registerMember" - 当然,Club 也可以调用它。 哈哈,没错。看起来如此明显,以至于我什至没有想到要写下来。谢谢:)

以上是关于C++ 中有没有办法将给定类的函数限制为仅另一个类(不使用继承,朋友)?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法将 Gmail 手表限制为仅添加的邮件?

有没有办法将 iPhone 应用程序限制为仅限某些型号?

在其派生类C++的构造函数中调用基类的构造函数[重复]

如何将 extjs 网格限制为仅显示 100 行并提供所有行作为下载?

C++中static函数类外定义的时候为啥不写static?

使Matlab类的属性成为给定集合中的一个元素[重复]