精华内容
下载资源
问答
  • C++ friend函数和friend

    2021-05-22 10:01:37
    类的friend函数(友元函数)在类的作用域之外定义,却具有访问类的非public(以及public)成员的权限。单独的函数或者整个类都可以被声明为另一个类的友元。使用friend函数可以提高程序的性能。定义友元函数:在类定义中...

    类的friend函数(友元函数)在类的作用域之外定义,却具有访问类的非public(以及public)成员的权限。单独的函数或者整个类都可以被声明为另一个类的友元。使用friend函数可以提高程序的性能。

    定义友元函数:

    在类定义中函数原型前面加保留字friend,就将函数声明为该类的友元。比如:要将类ClassTwo的所有成员函数声明为类ClassOne的友元,应在ClassOne定义中加入如下的形式的一条声明:

    friend class ClassTwo;

    注意点1:即使friend函数的原型在类定义内出现,友元仍然不是成员函数!

    注意点2:private ,public , protected 这些成员访问说明符标志与友元的声明无关,因此友元定义可以放到类的任何地方!

    注意点3:养成良好的编程友习惯,在类定义中把所有友元关系声明在最前面的位置,并且不要在其前面添加任何成员访问说明符!

    友元关系是授予的不是索取的!

    对于类B是类A的友元,类A必须显示的声明类B是他的友元,另外,友元关系既不是对称的也不是传递的,即如果类A是类B的友元,类B是类C的友元,不能够推断出类A是类C的友元(有缘关系不是传递的),也不能推出类B是类A的友元(友元关系不是对称的),同时也不能推出类C是类B的友元(友元关系不是对称的)。

    举例说明(使用friend函数修改类的private数据):

    在这个例子中,定义friend函数setX来设置Count类的private数据成员x。请注意,友元声明首先出现在类的定义中,甚至出现在public成员函数之前。再次说明,友元声明可以在类的任何地方。

    #include

    using namespace std;

    //Count 类定义

    class Count

    {

    friend void setX(Count &,int);//友元函数setX定义

    public:

    //构造函数

    Count()

    :x(0)//初始化x为0

    {

    //空的函数体

    }

    void print()const

    {

    cout<

    }

    private:

    int x;

    };

    //setX函数可以更改count类的私有成员

    //因为setX被定义为count类的友元函数

    void setX(Count &c, int val)

    {

    c.x= val;//将val变量的值赋给count类c对象的x数据成员

    }

    int main()

    {

    Count counter;

    cout<

    counter.print();

    setX(counter,8);//使用友元函数

    cout<

    counter.print();

    system("pause");

    return 0;

    }

    其运行结果是:

    函数setX是一个c语言风格的独立函数——他不是Count类的成员函数。因此,对于counter对象,当setX被调用的时候,第41行将counter作为参数传递给setX,而不是语句 counter.setX(8) ;   一样的句柄来调用此函数。如果把友元的声明去掉,就会出现错误的信息,函数setX不能修改count类的私有成员。

    一般情况下,我们将上述该程序分割成如下的三个文件:

    一个头文件(比如:Count.h)包含Count类的定义,而在类的定义中又包含了friend函数setX的函数模型。一个执行文件(比如:Count.cpp)包含Count类成员函数的定义以及friend函数setX的定义。一个主函数(比如:main.cpp)含有main函数。

    以上就是有关friend友元的基础知识,希望对大家有帮助。

    展开全文
  • 友元函数和友元类在实际开发中较少使用,想快速学习C++的读者可以跳过本节。一个类中可以有 public、protected、private ...借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 privat...

    友元函数和友元类在实际开发中较少使用,想快速学习C++的读者可以跳过本节。

    一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。现在,我们来介绍一种例外情况——友元(friend)。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。

    fnend 的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些。我们会对好朋友敞开心扉,倾诉自己的秘密,而对一般人会谨言慎行,潜意识里就自我保护。在C++中,这种友好关系可以用 friend 关键字指明,中文多译为“友元”,借助友元可以访问与其有好友关系的类中的私有成员。如果你对“友元”这个名词不习惯,可以按原文 friend 理解为朋友。

    友元函数

    在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。

    友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。

    1) 将非成员函数声明为友元函数。

    请大家直接看下面的例子:

    #include

    using namespace std;

    class Student{

    public:

    Student(char *name, int age, float score);

    public:

    friend void show(Student *pstu); //将show()声明为友元函数

    private:

    char *m_name;

    int m_age;

    float m_score;

    };

    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }

    //非成员函数

    void show(Student *pstu){

    cout<m_name<m_age<m_score<

    }

    int main(){

    Student stu("小明", 15, 90.6);

    show(&stu); //调用友元函数

    Student *pstu = new Student("李磊", 16, 80.5);

    show(pstu); //调用友元函数

    return 0;

    }

    运行结果:

    小明的年龄是 15,成绩是 90.6

    李磊的年龄是 16,成绩是 80.5

    show() 是一个全局范围内的非成员函数,它不属于任何类,它的作用是输出学生的信息。m_name、m_age、m_score 是 Student 类的 private 成员,原则上不能通过对象访问,但在 show() 函数中又必须使用这些 private 成员,所以将 show() 声明为 Student 类的友元函数。读者可以亲自测试一下,将上面程序中的第 8 行删去,观察编译器的报错信息。

    注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。下面的写法是错误的:

    void show(){

    cout<

    }

    成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。

    2) 将其他类的成员函数声明为友元函数

    friend 函数不仅可以是全局函数(非成员函数),还可以是另外一个类的成员函数。请看下面的例子:

    #include

    using namespace std;

    class Address; //提前声明Address类

    //声明Student类

    class Student{

    public:

    Student(char *name, int age, float score);

    public:

    void show(Address *addr);

    private:

    char *m_name;

    int m_age;

    float m_score;

    };

    //声明Address类

    class Address{

    private:

    char *m_province; //省份

    char *m_city; //城市

    char *m_district; //区(市区)

    public:

    Address(char *province, char *city, char *district);

    //将Student类中的成员函数show()声明为友元函数

    friend void Student::show(Address *addr);

    };

    //实现Student类

    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }

    void Student::show(Address *addr){

    cout<

    cout<m_province<m_city<m_district<

    }

    //实现Address类

    Address::Address(char *province, char *city, char *district){

    m_province = province;

    m_city = city;

    m_district = district;

    }

    int main(){

    Student stu("小明", 16, 95.5f);

    Address addr("陕西", "西安", "雁塔");

    stu.show(&addr);

    Student *pstu = new Student("李磊", 16, 80.5);

    Address *paddr = new Address("河北", "衡水", "桃城");

    pstu -> show(paddr);

    return 0;

    }

    运行结果:

    小明的年龄是 16,成绩是 95.5

    家庭住址:陕西省西安市雁塔区

    李磊的年龄是 16,成绩是 80.5

    家庭住址:河北省衡水市桃城区

    本例定义了两个类 Student 和 Address,程序第 27 行将 Student 类的成员函数 show() 声明为 Address 类的友元函数,由此,show() 就可以访问 Address 类的 private 成员变量了。

    几点注意:

    ① 程序第 4 行对 Address 类进行了提前声明,是因为在 Address 类定义之前、在 Student 类中使用到了它,如果不提前声明,编译器会报错,提示'Address' has not been declared。类的提前声明和函数的提前声明是一个道理。

    ② 程序将 Student 类的声明和实现分开了,而将 Address 类的声明放在了中间,这是因为编译器从上到下编译代码,show() 函数体中用到了 Address 的成员 province、city、district,如果提前不知道 Address 的具体声明内容,就不能确定 Address 是否拥有该成员(类的声明中指明了类有哪些成员)。

    这里简单介绍一下类的提前声明。一般情况下,类必须在正式声明之后才能使用;但是某些情况下(如上例所示),只要做好提前声明,也可以先使用。

    但是应当注意,类的提前声明的使用范围是有限的,只有在正式声明一个类以后才能用它去创建对象。如果在上面程序的第4行之后增加如下所示的一条语句,编译器就会报错:

    Address addr;  //企图使用不完整的类来创建对象

    因为创建对象时要为对象分配内存,在正式声明类之前,编译器无法确定应该为对象分配多大的内存。编译器只有在“见到”类的正式声明后(其实是见到成员变量),才能确定应该为对象预留多大的内存。在对一个类作了提前声明后,可以用该类的名字去定义指向该类型对象的指针变量(本例就定义了 Address 类的指针变量)或引用变量(后续会介绍引用),因为指针变量和引用变量本身的大小是固定的,与它所指向的数据的大小无关。

    ③ 一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员。

    友元类

    不仅可以将一个函数声明为一个类的“朋友”,还可以将整个类声明为另一个类的“朋友”,这就是友元类。友元类中的所有成员函数都是另外一个类的友元函数。

    例如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。

    更改上例的代码,将 Student 类声明为 Address 类的友元类:

    #include

    using namespace std;

    class Address; //提前声明Address类

    //声明Student类

    class Student{

    public:

    Student(char *name, int age, float score);

    public:

    void show(Address *addr);

    private:

    char *m_name;

    int m_age;

    float m_score;

    };

    //声明Address类

    class Address{

    public:

    Address(char *province, char *city, char *district);

    public:

    //将Student类声明为Address类的友元类

    friend class Student;

    private:

    char *m_province; //省份

    char *m_city; //城市

    char *m_district; //区(市区)

    };

    //实现Student类

    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }

    void Student::show(Address *addr){

    cout<

    cout<m_province<m_city<m_district<

    }

    //实现Address类

    Address::Address(char *province, char *city, char *district){

    m_province = province;

    m_city = city;

    m_district = district;

    }

    int main(){

    Student stu("小明", 16, 95.5f);

    Address addr("陕西", "西安", "雁塔");

    stu.show(&addr);

    Student *pstu = new Student("李磊", 16, 80.5);

    Address *paddr = new Address("河北", "衡水", "桃城");

    pstu -> show(paddr);

    return 0;

    }

    第 24 行代码将 Student 类声明为 Address 类的友元类,声明语句为:

    friend class Student;

    有的编译器也可以不写 class 关键字,不过为了增强兼容性还是建议写上。

    关于友元,有两点需要说明:

    友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。

    友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。

    除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。

    展开全文
  • C++ 语言访问控制 - 友元 (friend) 在 C++ 语言中,我们使用访问说明符 (access specifiers) 加强类的封装性: 定义在 public 说明符之后的成员在整个程序内可被访问,public 成员定义类的接口。 定义在 private ...

    C++ 语言访问控制 - 友元 (friend)

    在 C++ 语言中,我们使用访问说明符 (access specifiers) 加强类的封装性:

    • 定义在 public 说明符之后的成员在整个程序内可被访问,public 成员定义类的接口。
    • 定义在 private 说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private 部分封装了 (即隐藏了) 类的实现细节。

    私有成员 (private member) 定义在 private 访问说明符之后的成员,只能被类的友元或者类的其他成员访问。数据成员以及仅供类本身使用而不作为接口的功能函数一般设为 private

    公有成员 (public member) 定义在 public 访问说明符之后的成员,可以被类的所有用户访问。通常情况下,只有实现类的接口的函数才被设为 public

    class Sales {
    public:
    	Sales() = default;
    	Sales(const std::string &s) : book_no(s) {}
    	Sales(const std::string &s, unsigned int n, double p) :
    		book_no(s), sold_num(n), revenue(p*n) {}
    	Sales(std::istream &);
    
    	std::string isbn() const { return book_no; }
    	Sales &combine(const Sales &);
    
    private:
    	double avg_price() const
    	{
    		return sold_num ? revenue / sold_num : 0;
    	}
    
    	std::string book_no;
    	unsigned int sold_num = 0;
    	double revenue = 0.0;
    };
    

    作为接口的一部分,构造函数和部分成员函数 (即 isbncombine) 紧跟在 public 说明符之后;而数据成员和作为实现部分的函数则跟在 private 说明符后面。

    一个类可以包含 0 个或多个访问说明符,而且对于某个访问说明符能出现多少次也没有严格限定。每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者到达类的结尾处为止。

    1. 使用 classstruct 关键字

    我们可以使用 classstruct 关键字中的任何一个定义类。唯一的一点区别是,structclass 的默认访问权限不太一样。

    类可以在它的第一个访问说明符之前定义成员,对这种成员的访问权限依赖于类定义的方式。如果我们使用 struct关键字,则定义在第一个访问说明符之前的成员是 public 的。如果我们使用 class 关键字,则这些成员是private 的。

    出于统一编程风格的考虑,当我们希望定义的类的所有成员是 public 的时,使用 struct。如果希望成员是 private 的,使用 class。使用 classstruct 定义类唯一的区别就是默认的访问权限。

    2. 友元

    友元 (friend) 是类向外部提供其非公有成员访问权限的一种机制。友元的访问权限与成员函数一样。友元可以是类,也可以是函数。

    类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元 (friend)。如果类想把一个函数作为它的友元,只需要增加一条以 friend 关键字开始的函数声明语句即可。一般来说,最好在类定义开始或结束前的位置集中声明友元。

    class Sales {
    	// 为 Sales 的非成员函数所做的友元声明
    	friend Sales add(const Sales &, const Sales &);
    	friend std::istream &read(std::istream &, Sales &);
    	friend std::ostream &print(std::ostream &, const Sales &);
    
    public:
    	Sales() = default;
    	Sales(const std::string &s) : book_no(s) {}
    	Sales(const std::string &s, unsigned int n, double p) :
    		book_no(s), sold_num(n), revenue(p*n) {}
    	Sales(std::istream &);
    
    	std::string isbn() const { return book_no; }
    	Sales &combine(const Sales &);
    
    private:
    	double avg_price() const
    	{
    		return sold_num ? revenue / sold_num : 0;
    	}
    
    	std::string book_no;
    	unsigned int sold_num = 0;
    	double revenue = 0.0;
    };
    
    // Sales 接口的非成员组成部分的声明
    Sales add(const Sales &, const Sales &);
    std::istream &read(std::istream &, Sales &);
    std::ostream &print(std::ostream &, const Sales &);
    

    友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束。

    封装有两个重要的优点:

    • 确保用户代码不会无意间破坏封装对象的状态。
    • 被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。

    一旦把数据成员定义成 private 的,类的作者就可以比较自由地修改数据了。当实现部分改变时,我们只需要检查类的代码本身以确认这次改变有什么影响。只要类的接口不变,用户代码就无须改变。如果数据是 public 的,则所有使用了原来数据成员的代码都可能失效,这时我们必须定位并重写所有依赖于老版本实现的代码,之后才能重新使用该程序。

    把数据成员的访问权限设成 private 还有另外一个好处,这么做能防止由于用户的原因造成数据被破坏。如果我们发现有程序缺陷破坏了对象的状态,则可以在有限的范围内定位缺陷:因为只有实现部分的代码可能产生这样的错误。因此,将查错限制在有限范围内将能极大地降低维护代码及修正程序错误的难度。

    尽管当类的定义发生改变时无须更改用户代码,但是使用了该类的源文件必须重新编译。

    3. 友元的声明

    友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明。为了使友元对类的用户可见,我们通常把友元的声明与类本身放置在同一个头文件中 (类的外部)。我们的 Sales 头文件应该为 readprintadd 提供独立的声明 (除了类内部的友元声明之外)。

    许多编译器并未强制限定友元函数必须在使用之前在类的外部声明。一些编译器允许在尚无友元函数的初始声明的情况下就调用它。不过即使你的编译器支持这种行为,最好还是提供一个独立的函数声明。这样即使你更换了一个有这种强制要求的编译器,也不必改变代码。

    4. 友元高阶

    类可以把其他的类定义成友元,也可以把其他类 (之前已定义过的) 的成员函数定义成友元。友元函数能定义在类的内部,这样的函数是隐式内联的。

    4.1 类之间的友元关系

    Window 类的某些成员可能需要访问它管理的 Screen 类的内部数据。假设我们需要为Window 添加一个名为 clear 的成员,它负责把一个指定的 Screen 的内容都设为空白。为了完成这一任务,clear 需要访问 Screen 的私有成员。而要想令这种访问合法,Screen 需要把 Window 指定成它的友元。

    class Screen {
    	// Window 的成员可以访问 Screen 类的私有部分
    	friend class Window;
    	......
    };
    

    如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有 成员。通过上面的声明,Window 被指定为 Screen 的友元,因此我们可以将 Windowclear 成员写成如下的形式。

    class Window {
    public:
    	// 窗口中每个屏幕的编号
    	using ScreenIndex = std::vector<Screen>::size_type;
    
    	// 按照编号将指定的 Screen 重置为空白
    	void clear(ScreenIndex);
    
    private:
    	std::vector<Screen> screens{ Screen(24, 80, ' ') };
    };
    
    void Window::clear(ScreenIndex i)
    {
    	// s 是一个 Screen 的引用,指向我们想清空的那个屏幕
    	Screen &s = screens[i];
    
    	// 将那个选定的 Screen 重置为空白
    	s.contents = std::string(s.height * s.width, ' ');
    }
    

    首先把 s 定义成 screens vector 中第 i 个位置上的 Screen 的引用,随后利用Screenheightwidth 成员计算出一个新的 string 对象,并令其含有若干个 空白字符,最后我们把这个含有很多空白的字符串赋给 contents 成员。

    如果 clear 不是 Screen 的友元,上面的代码将无法通过编译,因为此时 clear 将不能访问 Screenheightwidthcontents 成员。而当 ScreenWindow 指定为其友元之后,Screen 的所有成员对于 Window 就都变成可见的了。

    友元关系不存在传递性。如果 Window 有它自己的友元,则这些友元并不能理所当然地具有访问 Screen 的特权。每个类负责控制自己的友元类或友元函数。

    4.2 令成员函数作为友元

    除了令整个 Window 作为友元之外,Screen 还可以只为 clear 提供访问权限。当把一个成员函数声明友元时,我们必须明确指出该成员函数属于哪个类。

    class Screen {
    	// Window::clear 必须在 Screen 类之前被声明
    	friend void Window::clear(ScreenIndex);
    	......
    };
    

    要想令某个成员函数作为友元,我们必须仔细组织程序的结构以满足声明和定义的彼此依赖关系。

    • 首先定义 Window 类,其中声明 clear 函数,但是不能定义它。在 clear 使用 Screen 的成员之前必须先声明 Screen
    • 接下来定义 Screen,包括对于 clear 的友元声明。
    • 最后定义 clear,此时它才可以使用 Screen 的成员。

    4.3 函数重载和友元

    尽管重载函数的名字相同,但它们仍然是不同的函数。如果一个类想把一组重载函数声明成它的友元,它需要对这组函数中的每一个分别声明。

    // 重载的 StoreOn 函数
    extern std::ostream &StoreOn(std::ostream &, Screen &);
    extern BitMap &StoreOn(BitMap &, Screen &);
    
    class Screen {
    	// StoreOn 的 ostream 版本能访问 Screen 对象的私有部分
    	friend std::ostream &StoreOn(std::ostream &, Screen &);
    	// ...
    };
    

    Screen 类把接受 ostream&StoreOn 函数声明成它的友元,但是接受 BitMap&作为参数的版本仍然不能访问 Screen

    4.4 友元声明和作用域

    类和非成员函数的声明不是必须在它们的友元声明之前。当一个名字第一次出现在一个友元声明中时,我们隐式地假定该名字在当前作用域中是可见的。友元本身不一定真的声明在当前作用域中。

    甚至就算在类的内部定义该函数,我们也必须在类的外部提供相应的声明从而使得函数可见。换句话说,即使我们仅仅是用声明友元的类的成员调用该友元函数,它也必须是被声明过的。

    struct X {
    	friend void f() { /* 友元函数可以定义在类的内部 */ }
    	X() { f(); }  // 错误:f 还没有被声明
    	void g{};
    	void h();
    };
    
    void X::g() { return f(); }  // 错误:f 还没有被声明
    void f();  // 声明那个定义在 X 中的函数
    void X::h() { return f(); }  // 正确:现在 f 的声明在作用域中了
    

    关于这段代码最重要的是理解友元声明的作用是影响访问权限,它本身并非普通意义上的声明。

    References

    (美) Stanley B. Lippman, (美) Josée Lajoie, (美) Barbara E. Moo 著, 王刚, 杨巨峰 译. C++ Primer 中文版[M]. 第 5 版. 电子工业出版社, 2013.
    https://www.informit.com/store/c-plus-plus-primer-9780321714114

    展开全文
  • 现在,我们来介绍一种例外情况——友元(friend)。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。friend 的意思是朋友,或者说是好友,与好友的关系显然要比一般...

    在C++中,一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。现在,我们来介绍一种例外情况——友元(friend)。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。

    friend 的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些。我们会对好朋友敞开心扉,倾诉自己的秘密,而对一般人会谨言慎行,潜意识里就自我保护。在 C++ 中,这种友好关系可以用 friend 关键字指明,中文多译为“友元”,借助友元可以访问与其有好友关系的类中的私有成员。如果你对“友元”这个名词不习惯,可以按原文 friend 理解为朋友。

    友元函数

    在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。

    友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。

    1) 将非成员函数声明为友元函数。

    请大家直接看下面的例子:

    #include <iostream>
    using namespace std;
    
    class Student{
    public:
        Student(char *name, int age, float score);
    public:
        friend void show(Student *pstu);  //将show()声明为友元函数
    private:
        char *m_name;
        int m_age;
        float m_score;
    };
    
    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    
    //非成员函数
    void show(Student *pstu){
        cout<<pstu->m_name<<"的年龄是 "<<pstu->m_age<<",成绩是 "<<pstu->m_score<<endl;
    }
    
    int main(){
        Student stu("小明", 15, 90.6);
        show(&stu);  //调用友元函数
        Student *pstu = new Student("李磊", 16, 80.5);
        show(pstu);  //调用友元函数
    
        return 0;
    }
    
    //运行结果:
    //小明的年龄是 15,成绩是 90.6
    //李磊的年龄是 16,成绩是 80.5

    show() 是一个全局范围内的非成员函数,它不属于任何类,它的作用是输出学生的信息。m_name、m_age、m_score 是 Student 类的 private 成员,原则上不能通过对象访问,但在 show() 函数中又必须使用这些 private 成员,所以将 show() 声明为 Student 类的友元函数。


    注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。下面的写法是错误的:

    void show()
    {
        cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
    }
    
    
    
    
    
    成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
    

    2) 将其他类的成员函数声明为友元函数

    friend 函数不仅可以是全局函数(非成员函数),还可以是另外一个类的成员函数。请看下面的例子:

    #include <iostream>
    using namespace std;
    
    class Address;  //提前声明Address类
    
    //声明Student类
    class Student{
    public:
        Student(char *name, int age, float score);
    public:
        void show(Address *addr);
    private:
        char *m_name;
        int m_age;
        float m_score;
    };
    
    //声明Address类
    class Address{
    private:
        char *m_province;  //省份
        char *m_city;  //城市
        char *m_district;  //区(市区)
    public:
        Address(char *province, char *city, char *district);
        //将Student类中的成员函数show()声明为友元函数
        friend void Student::show(Address *addr);
    };
    
    //实现Student类
    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    void Student::show(Address *addr){
        cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
        cout<<"家庭住址:"<<addr->m_province<<"省"<<addr->m_city<<"市"<<addr->m_district<<"区"<<endl;
    }
    
    //实现Address类
    Address::Address(char *province, char *city, char *district){
        m_province = province;
        m_city = city;
        m_district = district;
    }
    
    int main(){
        Student stu("小明", 16, 95.5f);
        Address addr("陕西", "西安", "雁塔");
        stu.show(&addr);
       
        Student *pstu = new Student("李磊", 16, 80.5);
        Address *paddr = new Address("河北", "衡水", "桃城");
        pstu -> show(paddr);
    
        return 0;
    }
    
    //运行结果:
    //小明的年龄是 16,成绩是 95.5
    //家庭住址:陕西省西安市雁塔区
    //李磊的年龄是 16,成绩是 80.5
    //家庭住址:河北省衡水市桃城区


    本例定义了两个类 Student 和 Address,程序第 27 行将 Student 类的成员函数 show() 声明为 Address 类的友元函数,由此,show() 就可以访问 Address 类的 private 成员变量了。

    几点注意:
    ① 程序第 对Address 类进行了提前声明,是因为在 Address 类定义之前、在 Student 类中使用到了它,如果不提前声明,编译器会报错,提示'Address' has not been declared类的提前声明和函数的提前声明是一个道理。

    ② 程序将 Student 类的声明和实现分开了,而将 Address 类的声明放在了中间,这是因为编译器从上到下编译代码,show() 函数体中用到了 Address 的成员 province、city、district,如果提前不知道 Address 的具体声明内容,就不能确定 Address 是否拥有该成员(类的声明中指明了类有哪些成员)。

    ③ 一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员。

     

    友元类

    不仅可以将一个函数声明为一个类的“朋友”,还可以将整个类声明为另一个类的“朋友”,这就是友元类。友元类中的所有成员函数都是另外一个类的友元函数。

    例如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。

    更改上例的代码,将 Student 类声明为 Address 类的友元类:

    #include <iostream>
    using namespace std;
    
    class Address;  //提前声明Address类
    
    //声明Student类
    class Student{
    public:
        Student(char *name, int age, float score);
    public:
        void show(Address *addr);
    private:
        char *m_name;
        int m_age;
        float m_score;
    };
    
    //声明Address类
    class Address{
    public:
        Address(char *province, char *city, char *district);
    public:
        //将Student类声明为Address类的友元类
        friend class Student;
    private:
        char *m_province;  //省份
        char *m_city;  //城市
        char *m_district;  //区(市区)
    };
    
    //实现Student类
    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    void Student::show(Address *addr){
        cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
        cout<<"家庭住址:"<<addr->m_province<<"省"<<addr->m_city<<"市"<<addr->m_district<<"区"<<endl;
    }
    
    //实现Address类
    Address::Address(char *province, char *city, char *district){
        m_province = province;
        m_city = city;
        m_district = district;
    }
    
    int main(){
        Student stu("小明", 16, 95.5f);
        Address addr("陕西", "西安", "雁塔");
        stu.show(&addr);
       
        Student *pstu = new Student("李磊", 16, 80.5);
        Address *paddr = new Address("河北", "衡水", "桃城");
        pstu -> show(paddr);
    
        return 0;
    }

    将 Student 类声明为 Address 类的友元类,声明语句为:

    friend class Student;

    有的编译器也可以不写 class 关键字,不过为了增强兼容性还是建议写上。

    关于友元,有两点需要说明:

    • 友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
    • 友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。

    除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。

    展开全文
  • Java中Friend概念的实现

    2021-03-16 20:30:33
    小编典典Java没有C++中的friend关键字。但是,有一种方法可以模拟这种情况。实际上可以提供更精确控制的方法。假设您具有类A和B。B需要访问A中的某些私有方法或字段。public class A {private int privateInt = ...
  • 全局函数做友元 #if 0 #include <iostream> #include <string> using namespace std;...//用friend关键字访问类中private的函数方法。... //2.在此用friend关键字声明!... friend void SeePrivate(Building...
  • 文章目录: 学习一下 一:c++静态成员(static) 二:c++友元、友元成员和友元类(friend) 三:c++运算符的重载(operator) 补充1:各种运算符重载的实例 补充2: 下面是不可重载的运算符列表 补充3:下面是可...
  • Graph Neural Networks for Friend Ranking in Large-scale Social Platforms基于图神经网络的大型社交平台好友排名 ABSTRACT 图形神经网络(GNNs)最近在图学习方面取得了重大进展。尽管GNNs具有丰富的表示能力,但...
  • 文章目录const访问权限限定static访问权限限定friend访问权限限定 以上三种关键字的限定,归根到底,都是this指针在幕后操作。 const访问权限限定 const对象可以调用非const成员函数吗? 非const对象可以调用...
  • friend.php

    2021-03-22 19:23:44
    session_start();//定义个常量,用来授权调用includes里面的文件...//定义个常量,用来指定本页的内容define('SCRIPT','friend');//引入公共文件require dirname(__FILE__).'/includes/common.inc.php';//判断是否...
  • 对于一个没有定义public访问权限的类,能够让其他的类操作它的私有成员往往是有用的。例如你写了一段binary tree的代码,Node是节点...C++中的friend关键字其实做这样的事情:在一个类中指明其他的类(或者)函数能够
  • C语言中friend友元函数详细解析友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend。我们已知道...
  • C++友元——friend

    2021-10-23 13:02:22
    友元的两种使用形式 友元函数、友元类。 1.友元函数(全局函数做友元) ... friend void Upgrade(Computer* compt); //全局函数做友元,可以放在类内任意位置 public: Computer() { this->cp...
  • Effective C++设计声明之(宁以non-member、non-friend替换member函数) 问题聚焦: member函数的封装一定比 non-member、non-friend好吗?并不是 导致较大封装性的可能是non-member non-friend函数,因为它并不增加...
  • friend用法

    2021-01-28 21:12:48
    c++ 的friend用法 1.友元的内容 友元的声明自带extern 所以友元的作用域自动提升到该类的作用域所以我们可以在类内部定义友元 2.普通成员函数作为友元 该友元可以作为操作符 //OpeClass.h #pragma once class ...
  • 关键字const 1、加类型前面加 const 2、如果不想改变一个变量的值,也...友元函数 friend 给类交朋友,朋友就可以访问该类的私有成员,这个编译器级别做的事情。 3、同样还有友元类,和友元函数道理是一样的 ...
  • 下载双人成行It Takes Two Friend’s Pass没反应的解决方法 在It Takes Two的游戏页面 点击“下载 It Takes Two Friend’s Pass”,如果没反应,只需直接在steam的搜索栏搜索“It Takes Two Friend’s Pass” 然后...
  • 友元声明 friend10. const 限定符10.1 const 与变量10.2 const 与指针10.3 const 与引用10.4 const 与函数的参数和返回值10.4 const 与类 9. 友元声明 friend 作用:非成员函数访问类中的私有成员 特点:友元关系...
  • namespace & friend

    2021-03-31 23:43:50
    friend 注意: 以下结果的编译命令都是编译命令都是 g++ -g -std=c++11 <sourcename> namespace foobar{ class foo{ }; void bar(const foo&){ } } int main(){ bar(foobar::foo()); return 0; } ...
  • C++面向对象:友元关键字friend的使用

    千次阅读 多人点赞 2021-05-23 20:32:50
    友元概述(需快速查阅看文末总结) ...在必要的时候可以使用friend关键字进行修饰访问. 具体用法如下: 全局函数做友元 全局函数是指在类外以及main函数之外的函数叫全局函数,作用域是全局. bedroom是
  • public: 程序的任何地方都可以访问.2 protected: 只有类本身及其派生类和其友元函数,友元类可以访问.3 private: 只有类本身及其友元函数,友元类可以访问.4 friend: 用于定义友元函数友元类. 在类里声明的一个普通...
  • 因此,C++ 就有了友元(friend)的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私。 友元分为两种:友元函数和友元类。 友元函数 在定义一个类的时候,可以把一些函数(包括...
  • How does one implement the friend concept in Java (like C++)?解决方案Java does not have the friend keyword from C++. There is, however, a way to emulate that; a way that actually gives a lot more ...
  • 设计模式之 C#为什么没有friend C#有friend多好呀 今天看一个视频在讲设计模式,思考起这个问题来了。我们在搭建框架的时候会用到一些设计模式,访问级别限制也是能用到的,比如单例模式让构造函数在外部访问不到就...
  • C++|friend,inline

    2021-09-06 10:04:35
    friend 在定义一个类时,可以把一些函数声明为“友元”。 即,友元函数,使用friend关键字进行声明。友元函数可以方位该类对象的私有成员。 写法:将全局函数声明为友元的写法: friend 返回值类型 函数名(参数...
  • 它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend。我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他...
  • 【C++】友元|friend class

    2020-12-23 15:49:21
    Friend Classes(友元类) 友元作用: 在一个类中指明其他的类(或者)函数能够直接访问该类中的private和protected成员。 在类的成员函数外部直接访问对象的私有成员。 你可以这样来指明: friend class a...
  • 这里有篇文章可以了解学习下: C++ friend 详细解析 仿函数 仿函数,又称为函数对象,是一个能行使函数功能的类,类内重载operator()运算符。 这里有篇文章可以了解下: 仿函数 这篇文章中的例子非常方便我们理解仿...
  • C语言中friend友元函数详细解析友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend。我们已知道...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 190,411
精华内容 76,164
关键字:

friend

友情链接: 00227269.rar