在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
{
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也就建立起来了。