C++ friend class与namespace冲突引起的思考

在C++中,friend class作为一种特殊的机制可以达到访问外部类私有成员的目的,因为这在某种程度上破坏了面向对象的封装性,所以friend class的应用场景非常有限。在组里的代码中,由于测试类(we call it TestSuite.cpp)需要访问被测试类的私有方法或者私有成员,而by default私有成员和函数对外是不可见的,那使用friend class也就是顺利成章的事情。

以下为一个具体实例:

class Manager
{
    public:
      Manager* getInstance(); 
      friend class ManagerTesterSuite; //declaration of friend class
    private:
      bool execute();
};

class ManagerTesterSuite
{
    public:
       void runTest() {
         Manager* manager = Manager::getInstance();
         Assert(manager.execute());
       }
        
}


以上例子就是一个简单的说明。不过程序的世界总是会有各种复杂的情况,今天为一个类写了测试类却总是通不过编译,错误是测试类无法访问对象类的私有成员,经过一番思考,我认为问题出在friend class的声明方式上,就是说test class并没有被正确的证明为对象类的friend class,因此它没有权限来访问对象类的私有方法。经过仔细对照,问题出在两个class存在于不同的namespace,如下:

 

 

//Manager.h 

 

namespace A {
class Manager 

{

  public:

    Manager* getInstance();

    friend class ManagerTestSuite;

  private:

    bool execute();

};

}

//ManagerTestSuite.h

class ManagerTestSuite

{
   private:
     void runTest() {
       Manager* manager = Manager::getInstance();
       Assert(manager->execute()); //failed to pass compiler here
     }
};

 

在网上搜索的相关文章之后,结论是在Manager中声明ManagerTestSuite是不可见的,因为Manager属于namespace A, 但ManagerTestSuite属于global namespace,解析错误。解决方法有两个: 1.把ManagerTestSuite也放到同一个namespace中,变成 namespace A { class ManagerTestSuite {...}; }; 方法二是在Manager class中正确声明ManagerTestSuite,包括前置声明ManagerTestSuite,毕竟在声明friend class的时候明确说明global namespace,这个代码应该如下:

//Manager.h
class ManagerTestSuite; //前置声明
namespace A
{
  class Manager 
  {
     public:
       Manager* getInstance();
       friend class ::ManagerTestSuite; //显示声明为全局namespace 
     private:
       bool execute();
  };
}

 

问题貌似解决了,但是我的疑问是,为什么子命名空间(namespace A)的class无法直接声明父命名空间(global namespace)中的class?认真搜索了下namespace的机制和friend class的用法。原来问题的关键在于在命名空间A中的class声明friend class的时候,相当于在namespace A中加入了一条friend class的前置声明,但是friend class不存在于空间A中,所以前置声明无效,从而friend class的声明也就失效了。换句话说,friend class在子命名空间是可见的,但是做前置声明的时候还是需要将完整的namespace路径搞对,在这个例子中,需要显示的表明friend class存在于global namespace中。因此,解决办法就是显示的在namespace A外面前置声明friend class,并且在声明friend class的时候显示标明是"::"全局空间下的class,这样就全部打通了,friendship也就建立起来了。

posted on 2012-09-21 03:03  梁霄  阅读(2476)  评论(0)    收藏  举报

导航