繁体   English   中英

对于 C++ 中的嵌套类,您如何访问封闭类的私有成员?

[英]For a Nested class in C++ how do you access the private member of enclosed class?

我是 C++ 的初学者,我有一个关于 C++ 中嵌套类的问题,你如何访问封闭类的受保护或私有成员?

class A{
  
   class B{
       protected:
        B(){};

   };
   B var;  <===  error as the constructor B is protected  
};

一种解决方案是将 B 的构造函数公开,但这会暴露它的作用域,其他函数可以实例化它,这是我不想要的。 有什么办法来处理这个?

您可以使用friend类:

class A
{
    class B
    {
        friend class A;  //<-- makes A a friend of B

        protected:
            B(){};
    };

    B var; //<-- OK
};

我相信提出的问题并没有解决真正的问题。 这几乎是一个XY问题,但幸运的是它有足够的提示可以推断出真正的问题。 尽管如此,为了适当的利益,我将在解决我认为是真正的问题之前先解决所提出的问题。


您如何访问封闭类的受保护或私有成员?

与在类未嵌套时授予此类访问权限的方式相同:具有 protected 或 private 成员的类将另一个类(或函数)声明为friend 请注意,这有点像大锤的方法,因此建议首先考虑替代方案。

一种解决方案是将 B 的构造函数公开,但这暴露了它的作用域,其他函数可以实例化它,这是我不想要的。

不,它不会在A之外暴露任何东西。 您已声明Bprivate内类型A ,这意味着只有A可以参考B 根据您当前的设置,将A设为B的朋友和将B所有成员B公开在功能上没有区别。 在任何一种情况下, A都可以完全访问B并且A之外A任何人都不知道B存在。

虽然我怀疑B应该是公开的,但 OP 有几天时间来纠正这个细节。 此外,在指出这种可能的疏忽后,OP 发布了一个新问题,因此我必须得出结论,OP 并未将其视为疏忽。


现在我们开始讨论我认为真正的问题:如何在不允许公共构造的情况下从外部类构造公共嵌套类。

有什么办法来处理这个?

如果B可能在某个时候成为公共类型(需要防范的合理事情),您应该考虑友谊的替代方案。 一种替代方法是将B的构造函数置于lock-and-key之下。 这可以扩展到B其他私有成员,但这可能不是必需的。

这种方法的一个优点是仅授予构造函数访问权限,而不授予所有私有成员访问权限。 这有助于保持封装。 此外,这种方法允许将构造传递给辅助函数,例如来自标准库的函数。 例如,标准容器的emplace_back方法可以构造一个B对象,只要该方法被赋予一个键。 这可能比依赖友谊更方便,具体取决于B对象的使用方式。

钥匙

“密钥”是A的私有类。 它很可能是一个空类,因为它不需要功能。 所有密钥需要做的是存在并且在A之外不可访问。

class A{
  private:
    class KeyForB {};

    // Rest of the definition of A
};

“锁”是一个接受“密钥”作为参数的构造函数。 这个参数不会被构造函数使用; 它所做的只是表示允许调用者调用构造函数。

    class B{
      public:
        /* explicit */ B(KeyForB /* unnamed */) : B() {}
      protected:
        B() {}
    };

有几个细节值得一看。 首先,您可能已经注意到B只是使用了A的私有成员! 如何? 有一条特殊规则表明嵌套类被视为外部类的一部分,因此它们可以访问外部类的私有(和受保护)成员——不需要friend声明。

其次,锁委托给受保护的构造函数。 这可能看起来很奇怪,但它确实是有目的的。 它不仅使B可以使用默认构造函数,而且还允许编译器优化掉锁和键。 考虑如果构造函数太大以至于编译器选择不内联它会发生什么。 如果函数未内联,则无法优化参数。 在这种情况下,这意味着需要构造KeyForB并将其放置在调用堆栈上。 相比之下,锁非常简单,在任何优化级别下都应该内联。 它被内联到对B()的调用,并且未使用的KeyForB从可执行文件中删除。

第三,有一条评论承认一些编码指南建议将大多数采用单个参数的构造函数标记为explicit 这是中肯的建议。 但是,在这种情况下,创建KeyForB对象的唯一原因是构造B对象。 因此,在这种情况下,保留此转换隐式可能是可以接受和方便的。

把它放在一起

剩下的唯一部分是为A编写一个构造函数,因为编译器提供的默认构造函数被删除,因为它是格式错误的。

class A{
  private:
    class KeyForB {};

  //public: // <-- possible future change
    class B{
      public:
        B(KeyForB) : B() {}
      protected:
        B() {}
    };

  public:
    // Default constructor
    A() : var(KeyForB{}) {}

  private:
    B var;
};

可能值得注意的是,如果var的类型从B更改为std::shared_ptr<B> ,则此方法不会遇到“ make_shared for friend class throwing error ”,因为为make_shared提供密钥将启用构造。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM