【问题标题】:Keeping part of public nested class visible only to the nesting class保持公共嵌套类的一部分仅对嵌套类可见
【发布时间】:2011-01-13 12:26:22
【问题描述】:

我在 C++ 中有一个嵌套类,它必须是公共的。但我需要它的一些方法对外部世界可见,其余的对嵌套类可见。那就是:

class set {
public:
    class iterator {
        innerMethod();
    public:
        outerMethod();
    }
}

我希望能够为 set 编写一个使用 innerMethod() 的方法。如果我把它公开,我也可以从外部访问它,这是我绝对不想要的。有没有办法在不做“朋友类设置”的情况下做到这一点?

提前致谢!

【问题讨论】:

  • 为什么不想使用friend 声明?
  • 不太清楚为什么用引号将其称为“朋友类集”。需要说明的是,您可以像使用 public 一样使用 friend,这似乎不会打扰您。
  • 这正是friend声明改进封装的那种情况,因为显而易见的替代方案是完全打破封装。
  • 也许在这种情况下,这与朋友或不是朋友无关。也许你的设计不好。请不要重新实现集合。 STL 库已经提供了一个。
  • @ ds27680 - 我需要在课堂上实现它。在现实生活中,如果我在 STL 库中有我自己的 Set 类,我绝不会编写它。

标签: c++ friend nested-class friend-class


【解决方案1】:

如果不使用friend 关键字,您就无法做到这一点。

在你说的评论中:

我目前在编程课上 采取,使用“朋友”据说是 不明智和普遍认为 大多数情况下“糟糕的编程”, 除非真的没有别的办法 周围。所以我尽量避免它 尽可能的。

friend 打破了封装,也许这就是你的班主任说这是糟糕的编程的原因。 但是成员函数也打破了封装,那你为什么要使用它们呢?为什么不避开它们呢? friend 以与成员函数相同的方式破坏封装; 因此,如果您对在需要时使用成员函数感到满意,那么您应该会感到满意在需要时也使用friend两者都存在于 C++ 中是有原因的!

class set {
public:
 class iterator 
 {
  friend class set; //<---- this gives your class set to access to inner methods!

  void innerMethod(){}
 public:
  void outerMethod(){}
 };
 iterator it;

 void fun()
 {
  it.innerMethod();
  it.outerMethod();
 }
};

看到这个:How Non-Member Functions Improve Encapsulation

【讨论】:

  • 我不会说不。而是“不容易”......我敢打赌我可以拥有私有方法,而不是使用朋友并从外部类调用它。但这肯定不是一个好主意……
  • 其实有。虽然这是相对保密的,但我已经在 SO 上提倡过几次。它也不特定于嵌套类。我添加了一个答案,希望能更接近我描述密钥使用的顶部,这与代理或倡导模式有点不同。
  • 封装的重点,不是阻止用户使用你的类,而是给它对类成员的受控访问,以及一组预定义的函数/方法来处理它,这以想要的方式完成,而不是用户。例如:在一个有理数类中,你会阻止用户直接访问分母,并给它一个承包商,当这个类的用户试图在分母中输入零时抛出异常。
  • @Mokingbird:是的。我没有说封装是为了“阻止”用户使用它。你的意思是什么?
【解决方案2】:

不,我认为除了使用 friend-directive 之外没有其他非 hacky 方法。

friend 就是为了这种目的而存在的,你为什么要避免呢?

【讨论】:

  • 在我目前上的编程课上,据说使用“朋友”是不明智的,并且在大多数情况下通常被认为是“糟糕的编程”,除非有真的没有其他办法。所以我尽量避免。
  • @Mokingbird: friend 打破了封装,也许这就是你的班主任说它的风格不好的原因。但是成员函数也打破了封装,你为什么要使用它们?
  • @Mokingbird:听起来是个提问的好机会。我很好奇为什么有人会说friendbad 编程习惯。显然,与其他任何事情一样,应该谨慎使用它,而不是启用构思不佳的 OO 设计,但在需要时使用它并没有本质上的错误。另请参阅C++ FAQ on friend
  • @Nawaz - 为什么成员函数会破坏封装?
  • @Nawaz,你错了,friend增强封装。那些说friend 风格不好的人并没有理解它的目的。确实,它并不经常有用,但有时确实有用。
【解决方案3】:

试试问:有没有什么方法可以不加两个数字相加? 对不起,如果我很苛刻,但朋友班正是为了这个......

【讨论】:

  • 是的,有。您可以使用按位运算符。不好的例子。 ;-)
【解决方案4】:

是的。

我一直在尝试提倡这种方法一段时间,基本思想是使用Key类。

虽然这实际上并没有删除friend的使用,但它确实减少了公开的实现细节集。

class set;

// 1. Define the Key class
class set_key: noncopyable { friend class set; set_key() {} ~set_key() {} };

class set
{

  // 2. Define the iterator
  class iterator
  {
  public:
    void public_method();

    void restricted_method(set_key&);

  }; // class iterator

}; // class set

现在,restricted_method 是公开的,因此set 不需要对iterator 进行任何特殊访问。然而,它的使用仅限于那些能够传递 set_key 实例的人......并且方便地只有 set 可以构建这样的对象。

请注意,set 实际上可能会将set_key 对象传递给它信任的其他人。它是传统意义上的钥匙:如果你把你公寓的钥匙交给某人,它可能会将它托付给另一个人。然而,由于键类的语义(不可复制,只有set 可以构造和销毁它),这通常仅限于key 对象范围的持续时间。

请注意,恶意黑客总是可能的,即*((set_key*)0)。这种方案可以防止墨菲,而不是马基雅维利(无论如何在 C++ 中是不可能的)。

【讨论】:

    【解决方案5】:

    你可以这样做:

    类集

    {

    public:
        class iterator
        {
            protected:
                iterator(){};
                virtual ~iterator(){};
    
            public:
                //outer world methods...
        };
    
    private:
        class privateIterator : public iterator
        {
            public:
                privateIterator(){};
                ~privateIterator(){}
    
            //inner methods;
        };
    
    public:
        iterator* CreateIterator()
        {
            return new privateIterator();//this is used to be sure that you only create private iterator instances
        }
    

    };

    我不知道这是否是正确的答案,但它现在确实使用朋友密钥工作并且它隐藏了一些方法。唯一的问题是你不能声明 privateIterator 并且你总是必须使用 CreateIterator 来创建一个实例......

    【讨论】:

      猜你喜欢
      • 2012-08-19
      • 2010-09-25
      • 2012-02-26
      • 2019-08-04
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      • 2012-11-30
      • 1970-01-01
      相关资源
      最近更新 更多