友元

  • 友元是一种允许非类成员函数访问类的非公有成员的一种机制。
  • 可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元。
  1. 友元函数
  2. 友元类

  •  友元函数在类作用域外定义,但它需要在类体中进行说明
  • 为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下:
    friend  类型 友元函数名(参数表);
  • 友元的作用在于提高程序的运行效率。

下面用程序来使用下友元:

复制代码
#include <iostream>
using namespace std;

class Point {
    friend double distance(const Point& p1, const Point& p2);//声明友员函数
public:
    Point(int x, int y);
private:
    int x_;
    int y_;
};

Point::Point(int x, int y):x_(x), y_(y) {

}

int main(void) {
    return 0;
}
复制代码

其中友员函数并不是类的成员函数,为了证明这点,我们先假设友员是成员函数,所以可以这样写:

编译一下:

友员的定义是在类体之外的,所以正确的实现友员函数的写法是去掉域访问修饰,如下:

既始在定义友元函数时在类体中实现实际上它也是在类体外:

下面来具体实现它,求两点之间的距离:

编译:

可见友元确实是可以访问类的私有变量,下面编写一下测试代码:

编译:

呃,这是啥子错,纠结了关天,最终发现是函数名的问题,可能是distance与系统函数重名了,所以需要修改一下,将首字母改为大写就OK了:

运行:

这就是友元使用的效果,在上面中说到“友元的作用在于提高程序的运行效率。”,何以见得呢?如果这个程序不用友元的话,则需要提供两个访问私有成员的函数,多了函数调用的开销。

  • 友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员(公有的、私有的、保护的),一般函数只能访问类中的公有成员。
  • 友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。
  • 某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。
  • 友元函数破坏了面向对象程序设计类的封装性,所以友元函数如不是必须使用,则尽可能少用。或者用其他手段保证封装性。

  • 如果某类B的成员函数会频繁的存取另一个类A的数据成员, 而A的数据成员的Private/Protectd限制造成B存取的麻烦, B只能通过A的Public的成员函数进行间接存取。
  • 把B做成A类的友元类,即A类向B类开发其Private/Protectd内容, 让B直接存取。
  • 友元类:一个类可以作另一个类的友元。
  • 友元类的所有成员函数都是另一个类的友元函数。
  • 友元类的声名:
    friend class 类名;

 下面来编写一个摇控器控制电视机的例子来说明友元类的使用:

复制代码
#include <iostream>
using namespace std;

//电视机
class Television {
public:
    Television(int volume, int chanel):volume_(volume),chanel_(chanel) {

    }
private:
    int volume_;//音量
    int chanel_;//频道
};

//摇控器
class TeleController {
public:
    void volumeUp(Television& tv) {
        tv.volume_ += 1;
    }

    void volumeDown(Television& tv) {
        tv.volume_ -= 1;
    }

    void chanelUp(Television& tv) {
        tv.chanel_ += 1;
    }

    void chanelDown(Television& tv) {
        tv.chanel_ -= 1;
    }
};

int main(void) {
    return 0;
}
复制代码

编译:

那友元类派上用场了:

复制代码
#include <iostream>
using namespace std;

//电视机
class Television {
    friend class TeleController;//将摇控器类声明为电视机的友员类,这样它的私有成员对于摇控器而言就全公开了
public:
    Television(int volume, int chanel):volume_(volume),chanel_(chanel) {

    }
private:
    int volume_;//音量
    int chanel_;//频道
};

//摇控器
class TeleController {
public:
    void volumeUp(Television& tv) {
        tv.volume_ += 1;
    }

    void volumeDown(Television& tv) {
        tv.volume_ -= 1;
    }

    void chanelUp(Television& tv) {
        tv.chanel_ += 1;
    }

    void chanelDown(Television& tv) {
        tv.chanel_ -= 1;
    }
};

int main(void) {
    return 0;
}
复制代码

再次编译:

这时编写测试代码:

这里就不打印了,重在说明友元类的使用,电视机与遥控器类是写在一个cpp文件中,如果单独写在不同的cpp文件中会怎么呢?下面来看下:

 

Television.h:

复制代码
#ifndef _TELEVISION_H_
#define _TELEVISION_H_

class Television
{
    friend class TeleController;
public:
    Television(int volume, int chanel);
private:
    int volume_;
    int chanel_;
};

#endif // _TELEVISION_H_
复制代码

Television.cpp:

#include "Television.h"

Television::Television(int volume, int chanel) : volume_(volume), chanel_(chanel)
{

}

TeleController.h:

复制代码
#ifndef  _TELE_CONTROLLER_H_
#define _TELE_CONTROLLER_H_

class TeleController
{
public:
    void VolumeUp(Television& tv);

    void VolumeDown(Television& tv);

    void ChanelUp(Television& tv);

    void ChanelDown(Television& tv);
};

#endif // _TELE_CONTROLLER_H_
复制代码

TeleController.cpp:

复制代码
#include "TeleController.h"

void TeleController::VolumeUp(Television& tv)
{
    tv.volume_ += 1;
}

void TeleController::VolumeDown(Television& tv)
{
    tv.volume_ -= 1;
}

void TeleController::ChanelUp(Television& tv)
{
    tv.chanel_ += 1;
}

void TeleController::ChanelDown(Television& tv)
{
    tv.volume_ -= 1;
}
复制代码

02.cpp:

#include <iostream>
using namespace std;

int main(void) {
    return 0;
}

这时编译:

报错了,说明Television对于TeleController类在这种情况下是不可见的:

再次编译:

实际上遥控器头文件中的电视机引用也可以用前向声明来搞,修改代码如下:

再在具体实现中包含头文件既可:

修改之后再编译,依然好使,那这种前向声明它是有好外的:TeleController.h头文件会相对而言小一些,如果直接包含Television.h头文件,如果在多个文件中包含TeleController.h文件则文件会增大。

  • 友元关系是单向的
    A是B的友元类(在B类中有friend class A),并不代表B也是A的友元类。
  • 友元关系不能被传递
    A是B的友元类,B又是C的友元类,并不代表A是C的友元类。
  • 友元关系不能被继承
    A是B的友元类,C继承A,并不代表C是B的友元类。

posted on   cexo  阅读(305)  评论(0编辑  收藏  举报

编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示