C++中友元的实例详解

C++中友元的实例详解

尽管友元被授予从外部访问类的私有部分的权限,但他们并不与面向对象的编程思想相悖;相反他提高了公共接口的灵活性。

一、友元类

友元声明可以位于公有、私有活保护部分、其所在位置无关紧要

我直接贴出一个摘自< c++ primer plus >的例子来演示 c++ 友元类

其中 Remote 为 Tv的友元类。

Tv.h

#ifndef TV_H_
#define TV_H_

/*一个类 电视 */
class Tv
{
public:
  friend class Remote; //Remote类可以访问Tv Privite 的私有部分
  enum {
    off,on  //开关
  };
  enum
  {
    MinVal,MaxVal=20  //音量
  };
  enum {
    Antena,Cable //使用的天线、还是电缆
  };
  enum
  {
    TV ,DVD  //工作模式
  };

  Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
    channel(5), 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 == Antena) ? Cable : Antena; }
  void set_input() { input = (input == TV) ? DVD : TV; }
  void settings()const; //显示所有设置

private:
  int state;  // 开或者 关
  int volume; // 音量
  int maxchannel; //最大
  int channel;  //当前频道
  int mode;  // 广播还是 电缆
  int input; //Tv 或者 DVD
};

/*Remote 的定义 (遥控器) */
class Remote {
private :
  int mode; // 控制 TV 或 DVD
public:
  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) { return t.onoff(); }
  void chanup(Tv & t) { return t.chanup(); }
  void chandown(Tv & t) { return t.chandown(); }
  void set_chan(Tv &t, int c) { t.channel = c; } //访问了Tv的私有成员
  void set_mode(Tv &t) { t.set_mode(); }
  void set_input(Tv &t) { t.set_input(); }
};
#endif // TV_H_

Tv.cpp

#include "stdafx.h"
#include "Tv.h"
#include <iostream>

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

bool Tv::voldown() {
  if (volume > MinVal) {
    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 {
  using std::cout;
  using std::endl;
  cout << "TV is " << (state == off ? "off" : "on") << endl;
  if (state == on) {
    cout << "Volume setting =" << volume << endl;
    cout << "Channel setting = " << channel << endl;
    cout << "Mode = " << (mode == Antena ? "antenna" : "cable") << endl;
    cout << "Input = " << (input == TV ? "TV" : "DVD") << endl;
  }
}

测试代码:

#include "stdafx.h"
#include "tv.h"
#include <iostream>

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

  cout << " \n Adjusted settings for 42 \" Tv: \n";
  s42.chanup();
  cout << "\n Adjusted settings for 42 \" Tv: \n";
  s42.settings();

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

  Tv s58(Tv::on);
  s58.set_mode();
  grey.set_chan(s58, 58);
  cout << " \n s58 \" setting: \n";
  s58.settings();

  system("pause");
  return 0;
}

运行结果:

Initial settings for 42 " Tv:
TV is off

 Adjusted settings for 42 " Tv:

 Adjusted settings for 42 " Tv:
TV is on
Volume setting =5
Channel setting = 7
Mode = cable
Input = TV

 s42 " settings after using remote:
TV is on
Volume setting =7
Channel setting = 10
Mode = cable
Input = TV

 s58 " setting:
TV is on
Volume setting =5
Channel setting = 58
Mode = antenna
Input = TV
请按任意键继续. . .

上述代码中将Remote类设置成为了Tv类的友元类,但事实上我们看到:唯一访问Tv的成员的方法是void set_chan(Tv &t, int c) { t.channel = c; } ,因此它是唯一需要友元的方法。因此不必让整个类成为友元,这就引出了我们下面要讲的的友元成员函数。

二、友元成员函数

我们要再Tv中将Remote::set_chan()设置成友元:

clas Tv
{
  friend void Remote::set_chan(Tv & t,int c ) ;
}

然而要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类。而 set_chan是这个类的方法。这意味着应将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而意味着Tv定义应当位于Remote定义之前,避开这种循环依赖的方法是,使用前向声明。
所以应该这样:

class Tv ; //前向声明
class Remote{...}
class Tv {...}

这里还有一个麻烦就是:

Remote 包含了内联代码例如:void onoff(Tv &t) {t.onoff();};

由于这将调用Tv的一个方法,所以编译器此时已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明的后面。这种问题的解决方法是:使用Remote声明中只包含方法声明,并将实际的定义放到Tv类之后。 所以最终应该这样:

class Tv; //前向声明
class Remote {...} //如要用到Tv 只能是方法声明
class Tv{...}
//接着写Remote的定义

这里通过方法定义中使用 inline关键字,仍然可以使方法称为内联方法
所以程序最终将tv.h改为:

#ifndef TV_H_
#define TV_H_
class Tv; //前向声明
class Remote {

public:
  enum {
    off, on  //开关
  };
  enum
  {
    MinVal, MaxVal = 20  //音量
  };
  enum {
    Antena, Cable //使用的天线、还是电缆
  };
  enum
  {
    TV, DVD  //工作模式
  };

private:
  int mode; // 控制 TV 或 DVD
public:
  Remote(int m = TV) :mode(m) {}
  //用到了Tv 只能是声明
  bool volup(Tv & t);
  bool voldown(Tv & t);
  void onoff(Tv & t);
  void chanup(Tv & t);
  void chandown(Tv & t);
  void set_chan(Tv &t, int c);
  void set_mode(Tv &t);
  void set_input(Tv &t);
};

class Tv
{
public:
  friend void Remote::set_chan(Tv & t,int c); //友元成员函数
  enum {
    off, on  //开关
  };
  enum
  {
    MinVal, MaxVal = 20  //音量
  };
  enum {
    Antena, Cable //使用的天线、还是电缆
  };
  enum
  {
    TV, DVD  //工作模式
  };
  Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
    channel(5), 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 == Antena) ? Cable : Antena; }
  void set_input() { input = (input == TV) ? DVD : TV; }
  void settings()const; //显示所有设置
private:
  int state;  // 开或者 关
  int volume; // 音量
  int maxchannel; //最大
  int channel;  //当前频道
  int mode;  // 广播还是 电缆
  int input; //Tv 或者 DVD
};

inline bool Remote::volup(Tv & t) { return t.volup(); }
inline bool Remote::voldown(Tv & t) { return t.voldown(); }
inline void Remote::onoff(Tv & t) { return t.onoff(); }
inline void Remote::chanup(Tv & t) { return t.chanup(); }
inline void Remote::chandown(Tv & t) { return t.chandown(); }
inline void Remote::set_chan(Tv &t, int c) { t.channel = c; }
inline void Remote::set_mode(Tv &t) { return t.set_mode(); }
inline void Remote::set_input(Tv &t) { return t.set_input(); }
#endif // TV_H_

测试结果不变。

*另外:也可一个将内联函数放在tv.cpp中,但必须去掉inline关键字,这样函数的连接性将成为外部的。

三、其他友元关系

1、上面的代码表示的是Remote是Tv的友元。但我们有时也会用到2个类互相友元。即Remote是Tv的友元,同时 Tv又是Remote的友元

他们定义与下面类似:

class Remote
class Tv
{
friend clas 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()是内联的,则应在一个单独的方法定义文件中定义它。

2、共同的友元。

需要使用友元的另一种情况是,函数需要访问两个类的私有数据。它可以是一个类的友元,同时是另一个类的友元。示例如下:

class Analyzer;
class Probe
{
  friend void sync (Analyzer & a,const Probe & p) ;
  friend void sync (Probe & p,const Analyzer & a);
  ...
};
class Analyzer
{
  friend void sync (Analyzer & a,const Probe & p) ;
  friend void sync (Probe & p,const Analyzer & a);
}
inline void sync (Analyzer & a,const Probe & p)
{
  ...
}
inline void sync (Probe & p,const Analyzer & a)
{
  ...
}

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • C++之友元:友元函数和友元类详解

    一.友元介绍我们知道,类的成员函数可以访问同类的其他成员函数,包括公有.私有和保护成员.而类的外部函数只能访问类的公有成员. 友元是一种允许非类成员函数访问类的非公有成员的一种机制.可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元. 友元函数友元类 二.友元函数友元函数在类作用域外定义,但它需要在类体中进行说明为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下: friend  类型 友元函数名(参数表);友元的作用在于提高程序的运行效率 友

  • 解析C++中不能重载为友元函数的四个运算符

    C++规定有四个运算符 =, ->, [], ()不可以是全局域中的重载(即不能重载为友员函数),这是为什么呢?现在先说说赋值运算符"="的重载C++规定赋值运算符"="只能重载为类的非静态成员函数,而不可以重载为类的友元函数.不能重载为类的静态成员应该比较容易理解,因为静态成员函数是属于整个类的,不是属于某个对象的,它只能去操作类静态数据成员.而赋值运算符"="是基于对象操作的.那么为什么赋值运算符不可以重载为类的友元函数?像同样都是双目

  • C++中的friend友元函数详细解析

    友元函数是可以直接访问类的私有成员的非成员函数.它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend. 我们已知道类具有封装和信息隐藏的特性.只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的.非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性.另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运

  • 有关C++继承与友元、继承与类型转换详解

    实例如下: #include <iostream> using namespace std; class a{ friend class pal; private: int i; protected: int j; public: int k; }; class b:public a{ protected: int l; }; class c:protected a{}; class d:private a{}; class e:public b{}; class pal{ public: v

  • C++友元函数与拷贝构造函数详解

    一.友元函数 1.友元函数概述: (1)友元函数是定义在一个类外的普通函数. 友元函数和普通函数的定义一样;在类内必须将该普通函数声明为友元. (2)友元函数不是成员函数. 不能通过对象来调用,而是直接调用;友元函数可以访问类的公有.受保护以及私有成员,但是必须通过对象.对象指针或者对象引用来访问. 2.友元函数的声明: friend 返回值类型 函数名(参数表); 在类中只需要将这个声明放置在公有部分即可. class Point { double x, y; public: Point(){

  • C++ 中友元函数与友元类详解

    C++ 中友元函数与友元类详解 总的来说,友元分为两类:友元函数与友元类.友元是针对类而言,它提供了一种非类的成员函数来访问类的非公有成员的一种机制.可以把一个函数指定为某类的友元,这个函数称为这个类的友元函数.也可以将类A指定为类B的友元,则类A是类B的友元类,类A的所有成员函数均是类B的友元函数,均可以访问类B的非公有成员.        友元函数的注意事项: (1)友元函数不是类的成员函数,在函数体中访问对象的成员,必须用"对象名.对象成员"方式来访问, 友元函数可以访问类中的所

  • C++友元(Friend)用法实例简介

    相对于Java而言,友元是C++中特有的一种元素,很多教材上对其介绍的相对较少,因此初学的时候往往不能很快掌握,本文总结了友元的用法和一些注意的地方,供大家参考借鉴.希望能对初学C++的朋友起到一点帮助作用. 操作步骤: 1)在MyFriend类中,将Father类定义成友元 2)写一个Son类继承自Father类 3)在Father类和Son类的构造函数中分别创建MyFriend对象,并定义其内部的三个变量 4)在MyFriend类的构造函数中创建Father对象,并定义其内部的三个变量 结果

  • C++中友元的实例详解

    C++中友元的实例详解 尽管友元被授予从外部访问类的私有部分的权限,但他们并不与面向对象的编程思想相悖:相反他提高了公共接口的灵活性. 一.友元类 友元声明可以位于公有.私有活保护部分.其所在位置无关紧要 我直接贴出一个摘自< c++ primer plus >的例子来演示 c++ 友元类 其中 Remote 为 Tv的友元类. Tv.h #ifndef TV_H_ #define TV_H_ /*一个类 电视 */ class Tv { public: friend class Remote

  • Angularjs中数据绑定的实例详解

    Angularjs中数据绑定的实例详解 这是一个最简单的angularjs的例子,关于数据绑定的,大家可以执行一下,看看效果 <html ng-app> <head> <title>angularjs-include</title> <script type="text/javascript" src="js/angular/angular.min.js"></script> </head

  • C++ 中构造函数的实例详解

    C++ 中构造函数的实例详解 c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初学者有所帮助. 1. 构造函数是干什么的 class Counter { public: // 类Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value = 0; } private: // 数据成员 int m_va

  • JSP Spring配置文件中传值的实例详解

    JSP Spring配置文件中传值的实例详解 通过spring提供方法,在配置文件中取传值 调用get方法  targetObject :指定调用的对象       propertyPath:指定调用那个getter方法 例1: public class Test1 { private String name = "nihao"; public String getName() { return name; } } Xml代码 <bean id="t1" cl

  • Linux 在Shell脚本中使用函数实例详解

    Linux 在Shell脚本中使用函数实例详解 Shell的函数 Shell程序也支持函数.函数能完成一特定的功能,可以重复调用这个函数. 函数格式如下: 函数名() { 函数体 } 函数调用方式: 函数名 参数列表 实例:编写一函数add求两个数的和,这两个数用位置参数传入,最后输出结果. root@ubuntu:/home/study# vi test3 #!/bin/bash add(){ a=$1; b=$2; z=`expr $a + $b`; echo "The sum is $z&

  • java 中匿名内部类的实例详解

    java 中匿名内部类的实例详解 原来的面貌: class TT extends Test{ void show() { System.out.println(s+"~~~哈哈"); System.out.println("超级女声"); } TT tt=new TT(); tt.show(); 只是说我们这里采用的是匿名的形式来处理. 重写了Test的show()方法,在重写好了以后,又调用了重写后的show()方法 实现代码: package cn.com; c

  • IOS 开发之swift中手势的实例详解

    IOS 开发之swift中手势的实例详解 手势操作主要包括如下几类 手势 属性 说明 点击 UITapGestureRecognizer numberOfTapsRequired:点击的次数:numberOfTouchesRequired:点击时有手指数量 设置属性 numberOfTapsRequired 可以实现单击,或双击的效果 滑动 UISwipeGestureRecognizer direction:滑动方向 direction 滑动方向分为上Up.下Down.左Left.右Right

  • Java中File的实例详解

    Java中File的实例详解 File 代表文件或者目录的类 构造函数 File(File parent,String child)---代表了指定父目录下的指定的子文件或者子目录 File(String pathname)---代表了指定路径对应的文件或者目录对象 重要方法 创建 createNewFile()---只能用来创建文件,并且一次只能创建一个文件,要求文件存储的目录必须真实存在 mkdir()---只能用来创建目录,不能用来创建多层目录 mkdirs()---创建多层目录 删除 d

  • C++类中的继承实例详解

    C++类中的继承实例详解 实例效果: 实现代码: #include<iostream> #include<string> using namespace std; class Person { public: Person(const char* name = "abc") :_name(name) { cout << "Person()" << endl; } Person(const Person& pp)

  • C++中 静态局部变量实例详解

     C++中 静态局部变量实例详解 以前经常使用和了解static作用在全局变量前,以至于甚至把static当做全局变量的代名词,但是其实static还可以作用于局部变量前 静态局部变量的意义: 1.分配空间在全局数据栈上 2.作用域只局限于当前的函数范围内(局部) 3.生命周期为整个程序,不会随着当前的函数结束而结束 4.首次初始化时赋值生效,以后的初始化赋值自动跳过 如 void fn() { static n=10; cout<<n<<endl; n++; } 这里的n是一个静

随机推荐