【问题标题】:Restrict method access to a specific class in C++限制对 C++ 中特定类的方法访问
【发布时间】:2009-12-03 21:57:19
【问题描述】:

我有两个密切相关的类,我将它们称为 Widget 和 Sprocket。 Sprocket 有一组方法,我希望它们可以从 Widget 调用,但不能从任何其他类调用。我也不想仅仅将 Widget 声明为 Spocket 的朋友,因为这将使 Widget 可以访问所有受保护和私有成员。我想限制 Widget 只能访问一组特定的方法。

我想出的一个解决方案是在 Sprocket 中创建一个嵌套类,其中包含这些方法的包装器,并使 Widget 成为这个嵌套类的朋友。例如:

class Sprocket
{
public:
    class WidgetInterface
    {
        friend class Widget;
        WidgetInterface(Sprocket* parent) : mParent(parent) {}

    private:
        void A() { mParent->A(); }
        void B() { mParent->B(); }

        Sprocket* mParent;
    };

private:
    void A() { ... }
    void B() { ... }
};

class Widget
{
public:
    Widget(Sprocket* sprock) : mSprocketIface(sprock) {}

    void doStuff() { mSprocketIface.A(); }  // Widget can call Sprocket::A()

private:
    Sprocket::WidgetInterface mSprocketIface;
};

这会导致一些代码重复,因为方法签名现在在两个地方声明,但它可以工作。但现在假设我想添加一个名为 SpecialWidget 的 Widget 子类,并且我希望该类也可以访问 Sprocket 方法。我可以简单地将这个新类添加到 Sprocket 朋友列表中,或者我可以在 Widget 中添加另一组受保护的包装器,SpecialWidget(和任何其他子类)可以访问,但是您可以看到这现在已成为维护问题。如果我添加新类或更改方法签名,我不想更新朋友列表或包装器。如果我使用“添加另一组包装器”的方法,方法签名将在三个地方重复!

有没有人知道更简单、更清洁的方法?

【问题讨论】:

标签: c++ inheritance


【解决方案1】:

如果您有两个紧密耦合的类,那么真的不值得尝试让friend 访问比它更细化。您可以控制两者的实现,并且您应该对自己有足够的信心,不会滥用调用某些严格来说不需要调用的方法的能力。

如果你想让未来的代码维护者更清楚,请在friend 声明中添加注释,解释它存在的原因(一般来说是个好主意),以及友元类允许调用哪些私有方法.

【讨论】:

  • 你是对的,最简单的解决方案是简单地使 Widget 和 Sprocket 的任何子类成为朋友。让我犯错的是,我必须为每个子类添加一个“朋友”声明,而在实际代码中有很多这样的声明。
【解决方案2】:

Sprocket 有一组方法,我希望它们可以从 Widget 调用,但不能从任何其他类调用。

为什么不在Widget 中为自己省点麻烦并实现这组方法,也许在这些方法中添加一个 Sprocket 参数?

【讨论】:

    【解决方案3】:

    我会将 WidgetInterface 实现为 Sprocket 继承的真实接口,因此 Widget 只知道 A 和 B。好的,其他人也可以使用该接口,但他们可能会有这样做的原因。

    【讨论】:

      【解决方案4】:

      秘密在于,所有这些访问控制都是毫无意义和虚幻的,没有办法真正限制对任何事物的访问。您只是使事情复杂化,并且很难弄清楚小部件的哪些部分可以使用,哪些部分不能使用。相反,让 widget 和 sprocket 的界面更加明显,也许让 widget 拥有一个私有的 sprocket。如果人们如此无知以至于他们会违反这一点,那么它就没有任何帮助,但是如果你做出一些可恶且难以弄清楚的事情,它保证即使是熟悉 C++ 的人也无法轻易使用它。

      【讨论】:

      • 可以限制对特定方法的访问,如上面的代码所示。但是,复杂性使代码难以维护。最后,我决定按照您的建议简化它,只需让 Widget 成为 Sprocket 的朋友,并将包装器添加到 Sprocket 的私有成员。现在 Widget 的任何子类都可以通过父类中的包装器访问 Sprocket 方法。它给了我大约 90% 的我想要的东西,而且没有所有的复杂性。
      • 任何类都可以将自己声明为任何事物的朋友或将其声明为任何命名空间的一部分,并且可以访问任何不会导致冲突错误的内存地址。如果他们有来源,你就无法阻止人们做愚蠢的事情。如果你真的想限制一切,你必须有一个封闭的源代码,并以头文件的形式提供一个有限的 API,只让他们看到你希望他们使用的确切定义。
      猜你喜欢
      • 1970-01-01
      • 2011-02-07
      • 2012-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      相关资源
      最近更新 更多