C++中友元类和嵌套类使用详解

目录
  • 前言
  • 1. 友元类
  • 2. 友元成员函数
  • 3. 其他友元关系
    • 3.1 成为彼此的友元类
    • 3.2 共同的友元
  • 4. 嵌套类
    • 嵌套类的作用域和访问控制
    • 作用域

前言

友元这个词,在学习类的时候肯定接触过,但是当时我们只用了很多友元函数。

友元有三种:

  • 友元函数
  • 友元类
  • 友元类方法

类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所以方法都能访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。

1. 友元类

假如我们有两个类:Tv电视机类,Remote遥控器类。那么这两个类是什么关系呢?既不是has-a关系,也不是 is-a关系,但是我们知道遥控器可以控制电视机,那么遥控器必须能够访问电视机的私有或保护数据。所以,遥控器就是电视机的友元。

类似于友元函数的声明,友元类的声明:

friend class Remote;

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

下面是代码实现:

//友元类1.h
#ifndef TV_H_
#define TV_H_
class Tv
{
private:
    int state;//On or Off
    int volume;//音量
    int maxchannel;//频道数
    int channel;//频道
    int mode;//有线还是天线,Antenna or Cable
    int input;//TV or 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(){input=(input==TV)?DVD:TV;}
    void settings() const;//display all settings
};
class Remote
{
private:
    int mode;//控制TV or 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){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();}
};
#endif
//友元类1.cpp
#include"友元类1.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==Antenna?"antenna":"cable")<<endl;
        cout<<"Input = "
            <<(input==TV?"TV":"DVD")<<endl;
    }
}
//友元类1main.cpp
#include<iostream>
#include"友元类1.h"
int main()
{
    using std::cout;
    Tv s42;
    cout<<"Initial setting for 42\" TV:\n";
    s42.settings();
    s42.onoff();
    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 remote:\n";
    s42.settings();
    Tv s58(Tv::On);
    s58.set_mode();
    grey.set_chan(s58,28);
    cout<<"\n58\" settings:\n";
    s58.settings();
    return 0;
}

PS D:\study\c++\path_to_c++> g++ -I .\include\ -o 友元类1 .\友元类1.cpp .\友元类1main.cpp
PS D:\study\c++\path_to_c++> .\友元类1.exe
Initial setting for 42" TV:  
Tv is Off

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

42" settings after using remote:
Tv is On
Volume setting = 7
Channel setting = 10
Mode = cable
Input = TV

58" settings:
Tv is On
Volume setting = 5
Channel setting = 28
Mode = antenna
Input = TV

总之,友元类和友元函数很类似,不需要过多说明了。

2. 友元成员函数

在上面那个例子中,我们知道大部分Remote方法都是用Tv类的公有接口实现的。这意味着这些方法不是真正需要作为友元。事实上,只有一个直接访问Tv的私有数据的Remote方法即Remote::chan(),因此它才是唯一作为友元的方法。我们可以选择仅让特定的类成员成为另一个类的友元,而不必让整个类成为友元,但这样做会有一些麻烦。

Remote::chan()成为Tv类的友元的方法是,在Tv类声明中将其声明为友元:

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

但是,编译器能处理这条语句,它必须知道Remote的定义。否则,它就不知道Remote::set_chan是什么东西。所以我们必须把Remote的声明放到Tv声明的前面。但是Remote声明中同样提到了TV类,那么我们必须把TV声明放到Remote声明的前面。这就发生了循环依赖。我们得使用 前向声明(forward declaration) 来解决这一问题。

class Tv;
class Remote{...};
class Tv{...};

但是,还有一点麻烦需要解决:Remote的类声明中不能直接给出成员函数的定义了,因为这些函数会访问Tv类成员,而Tv类的成员的声明是Remote类的后面的。那么我们必须在Remote的类声明外给出方法定义。

代码实现:

//友元成员函数1.h
#ifndef TVFM_H_
#define TVFM_H_
class Tv;
class Remote
{
public:
    enum{Off,On};
    enum{MinVal,MaxVal=20};
    enum{Antenna,Cable};
    enum{TV,DVD};
private:
    int mode;//控制TV or DVD
public:
    Remote(int m=TV):mode(m){};
    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
{
private:
    int state;//On or Off
    int volume;//音量
    int maxchannel;//频道数
    int channel;//频道
    int mode;//有线还是天线,Antenna or Cable
    int input;//TV or DVD
public:
    friend void Remote::set_chan(Tv &t,int c);
    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(){input=(input==TV)?DVD:TV;}
    void settings() const;//display all settings
};
inline bool Remote::volup(Tv & t){return t.volup();}
inline bool Remote::voldown(Tv & t){return t.voldown();}
inline void Remote::onoff(Tv &t){t.onoff();}
inline void Remote::chanup(Tv &t){t.chanup();}
inline void Remote::chandown(Tv &t){t.chandown();}
inline void Remote::set_chan(Tv &t,int c){t.channel=c;}
inline void Remote::set_mode(Tv &t){t.set_mode();}
inline void Remote::set_input(Tv &t){t.set_input();}
#endif

之前我们说过,内联函数的链接性是内部的,这就意味著函数定义必须在使用函数的文件中。在上面的代码中,内联函数的定义位于头文件中。当然你也可以将定义放在实现文件中,但必须删除关键字inline,这样函数的链接性将是外部的。

还有就是,我们直接让整个Remote类成为友元并不需要前向声明,因为友元语句已经指出Remote是一个类:

friend class Remote;

总之,不推荐使用友元成员函数,使用友元类完全可以达到相同的目的。

3. 其他友元关系

3.1 成为彼此的友元类

还是电视机和遥控器的例子,我们知道遥控器能控制电视机,但是我告诉你,现代电视机也是可以控制遥控器的。例如,我们现在可以在电视上玩角色扮演游戏,当你控制的角色从高处落入水中时,遥控器(手柄)会发出振动模拟落水感。那么,遥控器是电视机的友元,电视机也是遥控器的友元,那么它们互为友元。

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)
{
    ...
}

这里buzz函数的定义必须放到Remote类声明的后面,因为buzz的定义中会使用到Remote的成员。

3.2 共同的友元

使用友元的另一种情况是,函数需要访问两个类的私有数据,那么必须这样做:函数既是一个类的友元也是另一个类的友元.

例如,有两个类AnalyzerProbe,我们需要同步它们的时间成员:

class Analyzer;
class Probe
{
    friend void sync(Analyzer & a,const Probe &p);
    friend void sync(Probe &p,const Analyzer &a);
};
class Probe
{
    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)
{
    ...
}

4. 嵌套类

在C++中我们可以将类声明放在另一个类中。在另一个类中声明的类被称为嵌套类。

实际上,嵌套类很简单,它的原理和类中声明结构体、常量、枚举、typedef、名称空间是一样的,这些技术我们一直都在使用。

对类进行嵌套和包含是不一样的。包含意味著将类对象作为另一个类的成员,而对类进行嵌套不创建类成员,而是定义了一种类型,该类型仅在包含嵌套类的类中有效。

一般来说我们使用嵌套类是为了帮助实现另一个类,并避免名称冲突

嵌套类的作用域和访问控制

作用域

如果嵌套类是在另一个类的私有部分声明的,那么只能在后者的类作用域中使用它,派生类以及外部世界无法使用它。

如果嵌套类是在另一个类的保护部分声明的,那么只能在后者、后者的派生类的类作用域中使用该嵌套类,外部世界无法使用它。

如果嵌套类是在另一个类的公有部分声明的,那么能在后者、后者的派生类和外部世界中使用它。

class Team
{
public:
    class Coach{...}
    ...
};

上面的Coach就是一个公有部分的嵌套类,那么我们可以这样:

Team::Coach forhire;

总之,嵌套类的作用域和类中声明结构体、常量、枚举、typedef、名称空间是一样。但是对于枚举量来说,我们一般把它放在类的公有部分,例如ios_base类中的各种格式常量:ios_base::showpoint等。

访问控制

嵌套类的访问控制和常规类是一模一样的,嵌套类也有public,private,protected,只有公有部分对外部世界开放。

例如:

class A
{
    class B
    {
    private:
        int num;
    public
        void foo();
    };
};

则在A的类作用域中,可以创建B对象,并使用B.foo()方法。

看看一个类模板中使用嵌套类的例子:

#ifndef QUEUETP_H_
#define QUEUETP_H_
template<typename Item>
class QueueTP
{
private:
    enum{Q_SIZE=10};
    class Node
    {
    public:
        Item item;
        Node *next;
        Node(const Item & i):item(i),next(0){}
    };
    Node *front;
    Node *rear;
    int items;
    const int qsize;
    QueueTP(const QueueTP &q):qsize(0){}//抢占定义,赋值构造函数
    QueueTP & operator=(const QueueTP &q){return *this;}//抢占定义
public:
    QueueTP(int qs=Q_SIZE):qsize(qs)
    {
        front = rear =0;
        items=0;
    }
    ~QueueTP()
    {
        Node* temp;
        while (front !=0)
        {
            temp=front;
            front=front->next;
            delete temp;
        }
    }
    bool isempty() const
    {
        return items==0;
    }
    bool isfull() const
    {
        return items==qsize;
    }
    int queuecount() const
    {
        return items;
    }
    bool enqueue(const Item & item)
    {
        if(isfull())
            return false;
        Node * add = new Node(item);
        items++;
        if(front==0)
            front=add;
        else
            rear->next=add;
        rear=add;
        return true;
    }
    bool dequeue(Item &item)
    {
        if(front==0)
            return 0;
        item=front->item;
        items--;
        Node * temp=front;
        front=front->next;
        delete temp;
        if(items==0)
            rear=0;
        return true;
    }
};
#endif

到此这篇关于C++中友元类和嵌套类使用详解的文章就介绍到这了,更多相关C++友元类和嵌套类内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 简要介绍C++编程中的友元函数和友元类

    一个类中可以有 public.protected.private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员.现在,我们来补充介绍一个例外--友元(friend). fnend 的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些.有的家庭可能会这样处理:客厅对所有来客开放,而卧室除了本家庭的成员可以进人以外,还允许好朋友进入.在C++中,这种关系以关键宇 friend 声明,中文多译为友元.友元可以访问与其有好友关系的类

  • C++中的友元函数与友元类详情

    目录 一.问题背景 二.友元函数 三.友元类 一.问题背景 对类的封装是C++三大特性中的一个重要特性,封装好的数据在类的外部是访问不到的但是一旦出了问题,想要操作被封装的数据怎么办呢?由此友元函数友元类诞生了.在类中用friend关键字声明的函数或类,可以对类体中的任何权限成员属性进行操作有好处就有坏处友元函数.友元类严重破坏类的封装性,不到迫不得已不要使用. 二.友元函数 声明部分: friend 返回类型 函数名 (参数列表); 定义部分:返回类型 函数名 (参数列表){        函

  • 关于C++友元类的实现讲解

    C++中的友元既可以实现友元函数,也可以实现友元类,也就是说一个类也可以作为另外一个类的友元.当作为一个类的友元时,它的所有成员函数都是另一个类的友元函数,都可以访问另一个类的私有或者公有成员. 请看实例: #include <iostream> #include <cstring> using namespace std ; //声明教师类 class Techer ; //学生类 class Student { private: string name ; int age ;

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

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

  • 详解C++编程中的嵌套类的声明与其中的函数使用

    可以在一个类的范围内声明另一个类.这样的类称为"嵌套类". 嵌套类被视为在封闭类的范围内且可在该范围内使用.若要从嵌套类的即时封闭范围之外的某个范围引用该类,则必须使用完全限定名. 下面的示例演示如何声明嵌套类: // nested_class_declarations.cpp class BufferedIO { public: enum IOError { None, Access, General }; // Declare nested class BufferedInput.

  • C++嵌套类与局部类详细解析

    1. 嵌套类外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现.从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用.如果在外围类之外的作用域使用该类名时,需要加名字限定. 嵌套类中的成员函数可以在它的类体外定义. 嵌套类的成员函数对外围类的私有成员没有访问权,反之亦然. 嵌套类仅仅只是语法上的嵌入. 2. 局部类类也可以定义在函数体内,这样的类被称为局部类(loacl class).局部类只在定义它的局部域内可见. 局部类的成员

  • C++中友元类和嵌套类使用详解

    目录 前言 1. 友元类 2. 友元成员函数 3. 其他友元关系 3.1 成为彼此的友元类 3.2 共同的友元 4. 嵌套类 嵌套类的作用域和访问控制 作用域 前言 友元这个词,在学习类的时候肯定接触过,但是当时我们只用了很多友元函数. 友元有三种: 友元函数 友元类 友元类方法 类并非只能拥有友元函数,也可以将类作为友元.在这种情况下,友元类的所以方法都能访问原始类的私有成员和保护成员.另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元. 1. 友元类 假如我们有两个类:Tv电

  • Bottle框架中的装饰器类和描述符应用详解

    最近在阅读Python微型Web框架Bottle的源码,发现了Bottle中有一个既是装饰器类又是描述符的有趣实现.刚好这两个点是Python比较的难理解,又混合在一起,让代码有些晦涩难懂.但理解代码之后不由得为Python语言的简洁优美赞叹.所以把相关知识和想法稍微整理,以供分享. 正文 Bottle是Python的一个微型Web框架,所有代码都在一个bottle.py文件中,只依赖标准库实现,兼容Python 2和Python 3,而且最新的稳定版0.12代码也只有3700行左右.虽然小,但

  • c++中的单例类模板的实现方法详解

     1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

  • PHP中Closure类的使用方法及详解

    Closure,匿名函数,又称为Anonymous functions,是php5.3的时候引入的.匿名函数就是没有定义名字的函数.这点牢牢记住就能理解匿名函数的定义了. Closure 类(PHP 5 >= 5.3.0)简介 用于代表 匿名函数 的类. 匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象,下面我们来看一下PHP Closure类的使用方法及介绍. PHP Closure类之前在PHP预定义接口中介绍过,但它可不是interface哦,它是一个内部的final类.Clo

  • 对python中不同模块(函数、类、变量)的调用详解

    首先,先介绍两种引入模块的方法. 法一:将整个文件引入 import 文件名 文件名.函数名( ) / 文件名.类名 通过这个方法可以运行另外一个文件里的函数 法二:只引入某个文件中一个类/函数/变量 需要从某个文件中引入多个函数或变量时,用逗号隔开即可 from 文件名 import 函数名,类名,变量名 接下来,通过一个具体的例子说明引入 模块的具体方法: 假设新建一个python包test2,里边有一个名为run.py的python文件,run.py文件里有一个名为running()的函数

  • python 中不同包 类 方法 之间的调用详解

    目录结构如下: 在hello.py中导入ORM.py这个文件的时候,采用 import ORMPackage.ORM 或者 import ORM u = User(id = 123, name='codiy', email='codiy_huang@163.com', password='123456') 两种方式均报错 错误提示: name '***' is not defined 或者 No module named ORM 解决办法: 方法一 将包所在的目录添加到sys.path路径 im

  • Django中的模型类设计及展示示例详解

    django中设计数据模型类是基于ORM的对象关系映射更方便的进行数据库中的数据操作. 对象关系映射 把面向对象中的类和数据库表--对应,通过操作类和对象,对数表实现数据操作,不需要写sql,由ORM框架生成 django实现了ORM框架,在项目中与数据库之间产生桥梁作用 django数据库定义模型的步骤如下: python manage.py makemigrations python mange.py migrate 在应用models.py中编写模型类,继承models.Model类 在模

  • C++中使用function和bind绑定类成员函数的方法详解

    定义一个普通的类 class Test1{ public: void fun(int val){ cout<<"hello world "<<val<<endl; } }; 开始第一个测试 int main(){ Test1 t; function<void(int)> pf = std::bind(&Test1::fun,t,2); pf(4); // return 0; } 输出的值是2,说明pf传进去的4并没有什么用,在bi

  • Java中BigDecimal类的add()的使用详解

    Java中的BigDecimal类的使用: 使用Java中的BigDecimal可以进行精确的计算,但是在使用BigDecimal时我们需要注意它的add()方法,使用它自身的add( )方法并不会改变它原始的值,因为初始化BigDecimal是创建一个了个对象,使用add()方法时也等于是创建了一个对象,若要保存这个对象需要再创建一个对象. 句法: public BigDecimal add(BigDecimal val); public BigDecimal add(BigDecimal v

  • C++类中六个默认的成员函数详解

    浅谈 先来说一下"this指针": C++中通过引入this指针解决该问题,暨:C++编译器给每个"非静态的成员函数"增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问,只不过所有的操作对用户是透明的,暨用户不需要来传递,编译器自动完成. 说了这么多其实编译器在生成程序时获取对象首地址的信息.然后将获取的对象的首地址存放在了寄存器中,成员函数的其它参数都是存放在栈中.而this指针参数则是

随机推荐