这里整理学习一下C++中的友元函数,学习资料来自C++ primer。java
通常而言,咱们在类或者中定义了非公有成员,是为了避免但愿被其余任何外部的类访问,在Java中若是没有暴露任何的set()或者get()方法,那么这个非公有变量通常来讲对外是不能被访问到,而非公有方法也是相似如此,固然使用反射就另算了。然而在C++中提供了友元该关键字,提供了一种访问非公有成员的方式。c++
友元的定义是经过友元关键字类能够容许其余类或者函数访问他的非公有成员。若是一个类想把对应的函数做为友元,则只须要在函数体前面增长friend关键字便可。下面举个例子定义非成员函数为友元,简单先定义一个FriendTest.hpp头文件:函数
#ifndef FriendTest_hpp #define FriendTest_hpp class FriendTest{ friend int getAdd(); private: int add=23; public: FriendTest()=default; }; int getAdd(); #include <stdio.h> #endif /* FriendTest_hpp */
须要注意的是,经过friend关键字仅仅是指定了一个访问权限,并非函数声明,这点很重要,若是咱们但愿使用当前类的用户,即FriendTest类,调用某个友元函数,即上方的getAdd()方法,须要在友元声明以外再专门对函数进行一次声明。学习
为了使友元对类的用户可见,咱们一般把友元的声明与自己的类放置在同一个头文件当中,虽然编译器并无这么要求。code
友元函数定义在类的内部,是隐式内联的。blog
咱们直接在FriendTest.cpp中进行实现:作用域
#include "FriendTest.hpp" FriendTest friends; int getAdd(){ return friends.add; } #endif /* FriendVisitor_hpp */
经过如上方式就能够访问到对应的add变量,若是咱们去掉了FriendTest class中getAdd()的friend关键字,那么IDE会直接提示咱们有错的:get
须要注意的是,友元关系是不存在传递性的,若是A有本身的友元B,B也有本身的友元C,那么C是不能访问A中的非公有方法或者变量的。编译器
若是咱们要把整个类A当作友元来处理的话就须要在类B中将A声明为友元,例子以下,从新定义FriendTest:it
#ifndef FriendTest_hpp #define FriendTest_hpp class FriendTest{ friend class FriendVisitor; private: int add=23; int getAdd(){ return add; } public: FriendTest()=default; }; #endif /* FriendTest_hpp */ #endif /* FriendTest_hpp */
而且声明FriendVisitor类为其友元函数,在看下FriendVisitor.hpp的实现:
//FriendVisitor.hpp #ifndef FriendVisitor_hpp #define FriendVisitor_hpp #include "FriendTest.hpp" #include <stdio.h> class FriendVisitor{ public: int getTestAdd(); }; #endif /* FriendVisitor_hpp */
再看FriendVisitor.cpp实现:
#ifndef FriendVisitor_hpp #define FriendVisitor_hpp #include "FriendTest.hpp" #include <stdio.h> class FriendVisitor{ FriendTest test; public: int getTestAdd(){ test.add=44; return test.getAdd(); } }; #endif /* FriendVisitor_hpp */
能够看到,经过在FriendTest.hpp声明了FriendVisitor类为其友元类后,在FriendVisitor.hpp的实现中咱们能够任意的访问FriendTest的非公有方法以及变量。
上面看到了使用非成员函数以及类当作对于类的友元,除了上述两种还有一种粒度的友元,即做用在成员函数上的友元,仍是来看例子,FriendTest.hpp:
#ifndef FriendTest_hpp #define FriendTest_hpp class FriendTest; class FriendVisitor{ public: FriendVisitor(){ } int getTestAdd(FriendTest& firendTest); }; class FriendTest{ friend int FriendVisitor::getTestAdd(FriendTest&); private: int add=23; int getAdd(){ return add; } public: void test(){ printf("hello"); } FriendTest()=default; }; int FriendVisitor::getTestAdd(FriendTest &firendTest){ firendTest.add=434; return firendTest.getAdd(); } #endif /* FriendTest_hpp */
经过上述方式便可在FriendVisitor.getTestAdd()中调用FriendTest的非公有函数或者变量。
尽管重载函数的名字相同,可是他们仍然是不一样的函数,若是一个类想把一组函数声明为他们的友元,他须要对这组函数每个分别进行声明:
#ifndef FriendTest_hpp #define FriendTest_hpp class FriendTest{ friend int getTestAdd(FriendTest&); friend long getTestAdd(FriendTest&,int); private: int add=23; int getAdd(){ return add; } public: void test(){ printf("hello"); } FriendTest()=default; }; int getTestAdd(FriendTest& test){ return test.getAdd(); } long getTestAdd(FriendTest& test,int value){ return static_cast<long>(test.getAdd()+value) ; } #endif /* FriendTest_hpp */
类和非成员函数的声明不是必须在他们友元声明以前。但一个名字第一次出如今一个友元声明中2时候,咱们隐式假定在当前做用域中是可见的,然而,友元自己不必定真的声明在当前做用域中。就算在类的内部定义该函数,也必须在类的外部提供相对应的函数实现。即,调用友元的函数时候,该友元对应的函数须要被实现。
上述为C++ Primer中的原话,理解起来有点难懂,其实我以为就是在该文章上面提到的:
经过friend关键字仅仅是指定了一个访问权限,并非函数声明。函数没有声明的话,编译器该怎么找到函数呢?这么一想就比较简单了。
能够看下书上给的例子助于理解:
struct X{ friend void f(); X(){ f();//错误的,f()没有声明 }; void g(); void h(); } void X::g(){ f();//错误,f()尚未声明 }; void f(){//声明f() }; void x::H(){ f();//正确,f()已经被声明了 }