C++ 友元详解【建议收藏】
🎈 作者:Linux猿
🎈 简介:CSDN博客专家🏆,华为云享专家🏆,C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!
🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬
1. 概念
采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该类的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型和安全性检查及调用的时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
2. 规则
友元关系不能被继承;
友元关系是单向的,不具有交换性。若类B 是类A 的友元,类A 不一定是类B的友元,要看在类中是否有相应的声明。
友元关系不具有传递性。若类B 是类A 的友元,类C 是B 的友元,类C 不一定是类A 的友元,同样要看类中是否有相应的申明;
友元声明的位置:友元声明以关键字friend 开始,它只能出现在类定义中。因为友元不是授权类的成员,所以它不受其所在类的声明区域public private 和protected 的影响。通常我们选择把所有友元声明组织在一起并放在类头之后.
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元的本质是让其它不属于本类的成员(全局函数,其它类的成员函数),成为类的成员而具备了本类成员的属性。下面从友元函数和友元类讲解友元。
3. 友元函数
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,一个函数可以是多个类的友元函数,只需要在各个类中分别声明。其格式如下:
friend 类型 函数名(形式参数);
全局函数作为友元函数:
#include<iostream>
#include<cmath>
using namespace std;
class Point {
public:
Point(double xx, double yy) {
x = xx;
y = yy;
}
void Getxy();
friend double Distance(Point &a, Point &b);
private:
double x, y;
};
void Point::Getxy() {
cout << "(" << x << "," << y << ")" << endl;
}
double Distance(Point &a, Point &b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx*dx + dy*dy);
}
int main(void) {
Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.Getxy();
p2.Getxy();
double d = Distance(p1, p2);
cout << "Distance is" << d << endl;
return 0;
}
这里以一个栗子来解释策略模式,当周末来临,作为程序猿的你是打算出去浪呢,还是继续睡觉,还是去加班呢(当然是……)。
下面用代码来展示:
#include <iostream>
using namespace std;
class Strategy{ //策略基类
public:
Strategy(){}
virtual ~Strategy(){} //基类的析构函数最好是虚析构函数
virtual void Choose() = 0;
};
class Play: public: Strategy{ //游玩
public:
Play(){}
~Play(){}
void Choose(){
cout<<"Play ^_^"<<endl;
}
};
class Sleep: public: Strategy{ //睡觉
public:
Sleep(){}
~Sleep(){}
void Choose(){
cout<<"Sleep ~_~"<<endl;
}
};
class Work: public: Strategy{ //工作
public:
Work(){}
~Work(){}
void Choose(){
cout<<"Work >_<"<<endl;
}
};
class Weekend{ //周末
public:
Weekend(Strategy *_strategy):ch_strategy(_strategy){
}
~Weekend(){
delete ch_strategy;
}
void Choose(){
ch_strategy->Choose();
}
private:
Strategy *ch_strategy;
};
int main(){
Weekend *week_play = new Weekend(new Play());
Weekend *week_sleep = new Weekend(new Sleep());
Weekend *week_work = new Weekend(new Work());
week_play->Choose();
week_sleep->Choose();
week_work->Choose();
delete week_play;
delete week_sleep;
delete week_work;
return 0;
}
接下来我们看一下策略模式的定义:
它定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法变化不会影响到使用算法的客户。
从上面的代码中可以看出,即使你周末有更多的选择,如看电影、打豆豆、做头发等等,只需要继承策略类就可以,你只要去选择就可以。
包含的角色:
抽象策略角色(Strategy):抽象策略类;
具体策略角色(ConcreteStrategy):封装了继承相关的算法和行为;
环境角色(Context):持有一个策略类的引用,最终给客户端调用(相当于上面代码中的Weekend);
然后看一下它的UML图(以上面的程序为基础):
优点:
简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试;
避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展;
遵守大部分GRASP原则和常用设计原则,高内聚、低偶合;
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算
法类与使用算法类之间的耦合。
缺点:
因为每个具体策略类都会产生一个新类,所以会增加系统需要维护类的数量;
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象;
进一步看一下策略模式与简单工厂模式的结合:
#include <iostream>
using namespace std;
class Strategy{ //策略基类
public:
Strategy(){}
virtual ~Strategy(){} //基类的析构函数最好是虚析构函数
virtual void Choose() = 0;
};
class Play: public Strategy{ //游玩
public:
Play(){}
~Play(){}
void Choose(){
cout<<"Play ^_^"<<endl;
}
};
class Sleep: public Strategy{ //睡觉
public:
Sleep(){}
~Sleep(){}
void Choose(){
cout<<"Sleep ~_~"<<endl;
}
};
class Work: public Strategy{ //工作
public:
Work(){}
~Work(){}
void Choose(){
cout<<"Work >_<"<<endl;
}
};
class Weekend{ //周末
public:
Weekend(int type);
~Weekend(){}
void My_Hobby(){
ch_strategy->Choose();
}
private:
Strategy *ch_strategy;
};
Weekend::Weekend(int type){
switch(type){
case 0:
ch_strategy = new Play();
break;
case 1:
ch_strategy = new Sleep();
break;
case 2:
ch_strategy = new Work();
break;
default:
ch_strategy = NULL;
break;
}
}
int main(){
Weekend *week_play = new Weekend(0);
Weekend *week_sleep = new Weekend(1);
Weekend *week_work = new Weekend(2);
week_play->My_Hobby();
week_sleep->My_Hobby();
week_work->My_Hobby();
delete week_play;
delete week_sleep;
delete week_work;
return 0;
}
🎈 有任何疑问欢迎交流!
🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬
- 点赞
- 收藏
- 关注作者
评论(0)