C++ 友元學習

這裏整理學習一下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()已經被聲明瞭
}
相關文章
相關標籤/搜索