在类的外部,任何实例访问类的 private 或 protected 成员都是被禁止的,这是出于安全性的考虑。但是很多时候,出于实用性的考虑,的确需要从外部访问类的 private 或 protected 的成员,这可以在 C++ 中通过关键字 friend 来实现。
友元(friend)机制允许一个类将对其非公有成员(private 和 protected 成员)的访问授予指定的函数或类。友元函数必须要在类的内部进行声明,友元不是授予友元关系的那个类成员,出于编码风格的考虑,通常将友元声明橙组地放置在类定义的开头或结尾处
一、全局函数作为友元
// Friend_Global.cpp #include <iostream> using namespace std; class Base { int value; // 私有成员 protected: string str; // 保护成员 public: Base(const string newStr, const int ii) : str(newStr), value(ii) {} friend void view(Base&); // 声明friend的全局函数 }; void view(Base& b) { // 接受的是基类对象 cout << "string = " << b.str << endl << "value = " << b.value << endl; return ; } int main() { Base b("Base", 49); view(b); /* // 任何实例,在类的外部试图访问 // 类的private或protected成员都是被禁止的 cout << "string = " << b.str << endl << "value = " << b.value << endl; */ return 0; }
运行结果:
值得注意的是,使用friend关键字修饰的全局函数void view(Base& b)并非是Base类的成员函数,它仅仅是全局函数
二、其它类的成员函数成为友元
// Friend_Class.cpp #include <iostream> using namespace std; class Y; // 因为在类X中的view函数中出现了Y,因此必须事先声明类Y class X { public: /* 参数类型声明为指针,因为类Y的定义在下方, 在Y的定义之前,编译器或许并不知道应该为Y的对象 分配多大的内存空间,而如果是Y*指针的话,编译器 作了一次不完全的类型匹配,编译器知道如何传递一个 地址,这一地址有具体的大小,而不管被传递的是什么 对象,即使它还没有完全知道这种对象类型的大小 虽然事实上传递对象也是可以的,但是在这种情况下, 传递一个对象的地址更为安全! */ void view(const Y*); }; class Y { int value; // 私有成员 protected: string str; // 保护成员 public: Y(const string newStr, const int ii) : str(newStr), value(ii) {} friend void X::view(const Y*); // 声明X类的view方法为友元,允许在函数内部访问Y类的私有成员和保护成员 }; void X::view(const Y* y) { cout << "string = " << y->str << endl << "value = " << y->value << endl; return ; } int main() { Y y("Base", 49); X x; x.view(&y); return 0; }
运行结果: