友元

C++中,类有public、protected、private三种属性的成员,通过类对象可以访问public成员,只有本类的函数可以访问类的private成员。

#include <iostream>
using namespace std;

class A
{
public:
    A(){}
    ~A(){}
    void A1() {cout << "A1" << endl;}
    void An() {cout << "An" << endl; A2(); A3(); } // 可以在类函数中访问protected/private成员
protected:
    void A2() {cout << "A2" << endl;}
private:
    void A3() {cout << "A3" << endl;}
};

int main()
{
    A a;
    a.A1(); // 正确,类对象访问public成员函数
    a.An(); // 正确,类对象访问public成员函数
    // a.A2(); // 错误,不可以访问
    // a.A3(); // 错误,不可以访问
    return 0;
}


一种例外—友元(friend),通过友元,可以使其他类中的成员函数和全局范围内的函数访问当前类的private成员。

一、友元函数

  • 类的友元函数定义在类外部,但有权访问类的私有(private)成员和保护(protected)成员。
  • 尽管友元函数的原型在类的定义中出现过,但是友元函数并不是成员函数。
  • 友元可以是一个函数,该函数称为友元函数;友元也可以是一个类,该类称为友元类,在这种情况下,整个类及所有成员都是友元。
  • 如果要声明一个函数为一个类的友元,需要在类定义中将该函数原型前使用关键字friend。

友元函数的使用

因为友元函数没有this指针,则参数要有三种情况:

(1) 访问非static成员时,需要对象做参数;
(2) 访问static成员或全局变量时,则不需要对象做参数;
(3) 如果做参数的对象是全局对象,则不需要对象做参数.

可以直接调用友元函数,不需要通过对象或指针

【实例1: 将非成员函数声明为友元函数。】

#include <iostream>
using namespace std;

class A
{
public:
    A(){}
    ~A(){}
    void A1() {cout << "A1" << endl;}
    void An() {cout << "An" << endl; A2(); A3(); } // 可以在类函数中访问protected/protected成员
    friend void print(A &a); //类的友元函数
protected:
    void A2() {cout << "A2" << endl;}
private:
    void A3() {cout << "A3" << endl;}
};

void print(A &a)
{
    a.A2();
    a.A3();
}

int main()
{
    A a;
    // a.A1(); // 正确,类对象访问public成员函数
    // a.An(); // 正确,类对象访问public成员函数
    // // a.A2(); // 错误,不可以访问
    // // a.A3(); // 错误,不可以访问
    print(a);
    return 0;
}


// print() 是一个全局范围内的非成员函数,它不属于任何类。A2()是 A 类的 protected 成员,A3()是 A 类的 private 成员,原则上不能通过对象访问,但在 print() 函数中又必须使用这些 private 成员,所以将 print() 声明为 Student 类的友元函数。

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

void print(){
    A2();
    A3();
}

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

【实例2: 将其他类的成员函数声明为友元函数】

#include <iostream>
using namespace std;

class A; // 提前声明类A
/*
在 B 类中使用到了它,如果不提前声明,编译器会报错,提示’A’ has not been declared
类的提前声明和函数的提前声明是一个道理。
注意,类的提前声明的使用范围是有限的, 如果后增加如下一条语句,编译器就会报错:
A a;  //企图使用不完整的类来创建对象
*/

class B
{
public:
    B(){}
    ~B(){}
public:
    void B1(A &a);
};

class A
{
public:
    A(){}
    ~A(){}
public:
    friend void B::B1(A &a);
protected:
    void A2() {cout << "A2" << endl;}
private:
    void A3() {cout << "A3" << endl;}
};

void B::B1(A &a)
{
    a.A2();
    a.A3();
}

int main()
{
    A a;
    B b;
    b.B1(a);
    return 0;
}


二、友元类

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

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

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

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

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

posted on 2020-04-15 20:55  JJ_S  阅读(149)  评论(0编辑  收藏  举报