友元...

一、介绍

c++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但是有时这种限制比较严格,以至于不适合特定的编程问题。c++提供了另外一种形式的访问权限:有元函数,有三种:

  1. 友元函数;
  2. 友元类;
  3. 友元成员函数;

二、友元函数和友元成员函数

  1. 通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。

  2. 在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。

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

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

友元函数是指某些虽然不是类成员函数却能够访问类的所有成员的函数。类授予它的友元特别的访问权,这样该友元函数就能访问到类中的所有成员。
例:

#include<iostream>
using namespace std;
class Student
{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    Student(char *name,int age,float score);
    friend void showStudent(Student &pt);//声明友元函数
};
Student::Student(char *name, int age, float score):
    m_name(name),m_age(age),m_score(score){}

void showStudent(Student &pt)
{
    cout<<pt.m_name<<"年龄:"<<pt.m_age<<",成绩:"<<pt.m_score<<endl;

}
int main()
{
    Student ob("Tom",12,99.9);
    showStudent(ob);//调用友元函数
}

在这里插入图片描述
注:

  1. showStudent() 是一个全局范围内的非成员函数,它不属于任何类,它的作用是输出学生的信息。

  2. m_name、m_age、m_score 是 Student 类的 private 成员,原则上不能通过对象访问,但在 show() 函数中又必须使用这些 private 成员,所以将 show() 声明为 Student 类的友元函数。

  3. 友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。

void showStudent(Student &pt)
{
    cout<<pt.m_name<<"年龄:"<<pt.m_age<<",成绩:"<<pt.m_score<<endl;

}
  1. 成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员。

  2. show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。

2.类的某个成员函数 作为 另一个类的友元(友元成员函数)

  1. 使类GoodGay中的成员函数成为类Room的友元函数,这样类GoodGay的该成员函数就可以访问类Room的所有成员了。

  2. 当用到友元成员函数时,需注意友元声明和友元定义之间的相互依赖,在该例子中,类GoodGay必须先定义,否则类Room就不能将一个GoodGay的函数指定为友元。

  3. 只有在定义了类Room之后,才能定义类GoodGay的该成员函数。更一般的讲,必须先定义包含成员函数的类,才能将成员函数设为友元。另一方面,不必预先声明类和非成员函数来将它们设为友元。

#include<iostream>
#include<string>
using namespace std;
class Room;//Room向前声明
class GoodGay
{
public:
    void visit1(Room &room);//从上往下编译不是Roo类而是GoodGay,所以要将Room类提前声明;不能表明类有哪些成员
    void visit2(Room &room);
};
class Room
{
//如果想将visit2作为Room类的友元 那么Visit2就可以访问 Room的私有数据 
//一定要记得 加类作用域
    friend void GoodGay::visit2(Room &room);
private:
    string bedRoom;
public:
    string sittingRoom;
public:
    Room()
    {
        this->bedRoom="卧室";
        this->sittingRoom="客厅";
    }

};
void GoodGay::visit1(Room &room){
    cout<<"好基友visit1访问了你的"<<room.sittingRoom<<endl;
    //cout<<"好基友visit2访问了你的"<<room.bedRoom<<endl;//私有数据
}
void GoodGay::visit2(Room &room){
    cout<<"好基友visit2访问了你的"<<room.sittingRoom<<endl;
    cout<<"好基友visit2访问了你的"<<room.bedRoom<<endl;
}
int main()
{
    Room myRoom;
    GoodGay mygoodGay;

    mygoodGay.visit1(myRoom);//只能访问客厅
    mygoodGay.visit2(myRoom);//客厅,卧室都可以
    return 0;
}

在这里插入图片描述

三、友元类

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。

当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。
关于友元类的注意事项:

  1. 友元关系不能被继承。

  2. 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。

  3. 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的声明。

在类定义中声明友元类的写法如下:
friend class 类名;
例:

#include<iostream>
#include<string>
using namespace std;
class Room;
class GoodGay
{
public:
    void visit1(Room &room);
    void visit2(Room &room);
};
class Room
{
//将GoodGay作为Room的友元 
 //GoodGay 所有成员函数 都可以访问 Room私有数据
    friend class GoodGay;
private:
    string bedRoom;
public:
    string sittingRoom;
public:
    Room()
    {
        this->bedRoom="卧室";
        this->sittingRoom="客厅";
    }

};
void GoodGay::visit1(Room &room){
    cout<<"好基友visit1访问了你的"<<room.sittingRoom<<endl;
    cout<<"好基友visit2访问了你的"<<room.bedRoom<<endl;
}
void GoodGay::visit2(Room &room){
    cout<<"好基友visit2访问了你的"<<room.sittingRoom<<endl;
    cout<<"好基友visit2访问了你的"<<room.bedRoom<<endl;
}
int main()
{
    Room myRoom;
    GoodGay mygoodGay;

    mygoodGay.visit1(myRoom);
    mygoodGay.visit2(myRoom);
    return 0;
}

在这里插入图片描述

1.实例

模拟电视机和遥控器设计一个Tv类和一个Remote类,。分别表示电视机和遥控器。公有继承的is-a关系并不适用,私有继承和保护继承的has-a关系也不适用。事实上,遥控器可以改变电视机的状态,表明应将Remote类作为Tv的一个友元。
首先确定Tv类,状态:

  1. 开/关;
  2. 频道设置;
  3. 音量设置;
  4. 有线设置;
  5. 有线电视或天线调节模式;
  6. TV调或A/V输入。
#include <iostream>
using namespace std;

class Tv
{
private:
    int state;//on或off
    int volume;//假设数字化
    int maxchannel;//最大通道数
    int channel;//当前频道设置
    int mode;//广播或有线
    int input;//TV或DVD
public:
    friend class Remote;
    enum{Off,On};
    enum{MinVal,MaxVal=20};
    enum{Antenna,Cable};
    enum{TV,DVD};
    Tv(int s = Off,int mc=125):state(s),volume(5),
    maxchannel(mc),channel(2),mode(Cable),input(TV){}
    void onoff(){state=(state == On)?Off : On;}
    bool ison()const{return state == On;}
    bool volup();
    bool voldown();
    void chanup();
    void chandown();
    void set_mode(){mode = (mode == Antenna)? Cable : Antenna;}
    void set_input(){mode = (input == TV)? DVD : TV;}
    void settings()const;
};

class Remote
{
private:
    int mode;//控制TV或DVD
public:
//所有的Remote方法都将一个Tv对象引用作为参数,表明遥控器必须针对特定的电视机
    Remote(int m=Tv::TV):mode(m){}
    bool volup(Tv &t){return t.volup();}
    bool voldown(Tv & t){return t.voldown();}
    void onoff(Tv & t){t.onoff();}
    void chanup(Tv & t){t.chanup();}
    void chandown(Tv & t){t.chandown();}
    void set_chan(Tv & t,int c){t.channel = c;}
    void set_mode(Tv & t){t.set_mode();}
    void set_input(Tv & t){t.set_input();}

};

bool Tv::volup()
{
    if(volume<MaxVal)
    {
        volume++;
        return true;

    }else
        return false;
}
bool Tv::voldown()
{
    if(volume>MaxVal)
    {
        volume--;
        return true;
    }else
        return false;
}

void Tv::chanup()
{
    if(channel<maxchannel){
        channel++;
    }else
        channel=1;
}
void Tv::chandown()
{
    if(channel>1)
        channel--;
    else
        channel = maxchannel;
}

void Tv::settings()const
{
    cout<<"TV is"<<(state == Off?"Off":"On")<<endl;
    if(state == On)
    {
        cout<<"Volume setting = "<<volume<<endl;
        cout<<"Channel setting = "<<channel<<endl;
        cout<<"Mode = "<<(mode==Antenna?"antenna":"cable")<<endl;
        cout<<"Input = "<<(input == TV?"TV" : "DVD")<<endl;
    }
}


int main()
{
    Tv s42;
    cout<<"Initial setting for 42\" TV:\n";
    s42.settings();
    s42.onoff();
    s42.chanup();
    cout<<"\nAdjusted settings for 42\" TV:\n";
    s42.chanup();
    cout<<"\nAdjusted settings for 42\" TV:\n";
    s42.settings();


    Remote grey;
    grey.set_chan(s42,10);
    grey.volup(s42);
    grey.volup(s42);
    cout<<"\n42\" settings after using remode:\n";
    s42.settings();

    Tv s58(Tv::On);
    s58.set_mode();
    grey.set_chan(s58,28);
    cout<<"\n58\"settings:\n";
    s58.settings();
    return 0;
}

在这里插入图片描述

四、其他友元关系

  1. 交互式遥控器让你能够回答电视节目中的问题,如果回答错误,电视将在控制器产生嗡嗡声。
  2. 只看C++编程方面,新的方案将受益于相互的友情,一些Remote方法能够影响Tv对象,而一些Tv方法也能影响Remote对象。
  3. 可以通过让类彼此成为对方的友元来实现,即除了Remote是Tv的友元外,Tv还是Remote的友元。
  4. 对于使用Remote对象的Tv方法,其原型可在Remote类声明之前声明,但必须在Remote类声明之后定义,以便编译器有足够的信息来编译该编译方法。

实例:

class Tv
{
	friend class Remote;
	public:
	void buzz(Remote & r);
	...
	};
	class Remote
	{
	friend class Tv;
	public :
	void Bool volup (Tv & t){t.volup();}
	...
	};
	inline void Tv::buzz(Remote & r)
	{
	...
	}
//由于Remote的声明位于Tv声明的后面,所以可在声明中定义Remote::volup()。
//但TV::buzz()方法必须在Tv声明的外部定义,使其位于Remote声明的后面。
//如果不希望buzz()是内联的,则应在一个单独的方法定义文件中定义它。
五、共同的友元
  1. 需要使用友元的另一种情况是,函数需要访问两个类的私有数据
  2. 从逻辑上看这样的函数应是每个类的成员函数,但这是不可能的。它可以是一个类的成员,同时是另一个类的友元,但有时将函数作为两个类的友元更合理。

例如:
假定有一个Probe类和一个Analyer类,前者表示某种可编程的测量设备,后者表示某种可编程的分析设备。这两个类都有内部时钟,且希望它们能够同步,则应该包含下面代码:

class Analyzer;
class Probe
{
	frinend void sync(Analyzer & a,const Probe & p);//同步a到p
	friend void sync(Probe & p,const Analyzer & a);//同步p到a
	...
};
class Analyzer
{
	friend void sync(Analyzer & a,const Probe & p);//同步a到p
	friend void sync(Probe & p,const Analyzer & a);//同步p到a
	...
};
//定义友元函数
inline void sync(Analyzer & a,const Probe & p)
{
...
}
inline void sync(Analyzer & p,const Probe & a)
{
...
}

前面的声明使编译器看到Probe类声明中的友元声明时,知道Analyzer是一种类型。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值