信息
- 外文名
- friend
- 类 型
- 虚拟函数
- 中文名
- 友元函数
- 学 科
- 计算机科学
友元函数友元函数
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。()看下面表示有理数的一个类:class rational {public:rational(int numerator = 0,int denominator = 1);int numerator() const;int denominator() const;private:...};这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是和rational类相联系的,所以,写一个成员函数把这个操作包到类中。class rational {public:...const rational operator*(const rational& rhs) const;};(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)条款21: 尽可能使用const条款22: 尽量用“传引用”而不用“传值”条款23: 必须返回一个对象时不要试图返回一个引用可以很容易地对有理数进行乘法操作:rational oneeighth(1,8);rational onehalf(1,2);rational result = onehalf * oneeighth; // 运行良好result = result * oneeighth; // 运行良好但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:result = onehalf * 2; // 运行良好result = 2 * onehalf; // 出错!这是一个不好的苗头。记得吗?乘法要满足交换律。如果用下面的等价函数形式重写上面的两个例子,问题的原因就很明显了:result = onehalf.operator*⑵; // 运行良好result = 2.operator*(onehalf); // 出错!对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数2没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):result = operator*(2,onehalf); // 错误!但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款m19)。换句话说,编译器处理这个调用时的情形类似下面这样:const rational temp⑵; // 从2产生一个临时// rational对象result = onehalf * temp; // 同onehalf.operator*(temp);当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:class rational {public:explicit rational(int numerator = 0,// 此构造函数为int denominator = 1); // explicit...const rational operator*(const rational& rhs) const;...};那么,下面的语句都不能通过编译:result = onehalf * 2; // 错误!result = 2 * onehalf; // 错误!这不会为混合运算提供支持,但至少两条语句的行为一致了。然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面result的第一个赋值语句通过编译。实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:result = onehalf.operator*⑵; // converts int -> rational而这个语句不行:result = 2.operator*(onehalf); // 不会转换// int -> rational第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。尽管如此,你可能还是想支持混合型的算术操作,而实现的方法应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:class rational {... // contains no operator*};// 在全局或某一名字空间声明,// 参见条款m20了解为什么要这么做const rational operator*(const rational& lhs,const rational& rhs){return rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());}rational onefourth(1,4);rational result;result = onefourth * 2; // 工作良好result = 2 * onefourth; // 万岁,它也工作了!这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。让我们回头再来看看本书那个主要的例子,string类。如果想重载operator>>;和operator<<;来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:// 一个不正确地将operator>>;和// operator<<;作为成员函数的类class string {public:string(const char *value);...istream& operator>>(istream& input);ostream& operator<<(ostream& output);private:char *data;};string s;s >> cin; // 合法,但// 有违常规s << cout; // 同上这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。istream& operator>>(istream& input,string& string){delete [] string.data;read from input into some memory,and make string.datapoint to itreturn input;}ostream& operator<<(ostream& output,const string& string){return output << string.data;}注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。假设f是想正确声明的函数,c是和它相关的类:·虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。·operator>>;和operator<<;决不能是成员函数。如果f是operator>>;或operator<<;,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。·只有非成员函数对最左边的参数进行类型转换。如果f需要对最左边的参数进行类型转换,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。·其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
-
友元函数
2021-02-02 10:58:19为什么要使用友元函数 当类想要实现一个功能,而他本身又完成不了的时候,需要借助外力来完成,这时候设置一个友元函数来完成.友元函数拥有类的一切权限,提高了执行效率 友元函数的俩种使用方式 友元函数,友元类 使用...为什么要使用友元函数
当类想要实现一个功能,而他本身又完成不了的时候,需要借助外力来完成,这时候设置一个友元函数来完成.友元函数可以访问类里的所有数据,提高了执行效率
友元函数的俩种使用方式
友元函数,友元类
使用全局函数作为友元函数
这里设计了一个CPU升级的函数,因为电脑本身是无法自己给自己升级的,所以就要借助外力,当然也可以定义接口写
这里的实现友元函数也很简单,只要把声明放入类里就行,public和private都一样
#include <iostream> #include <string> #include <sstream> //把全局函数设为友元函数 //设一个升级CPU的函数 class computer { public: computer(); std::string description(); friend void upgrade(computer* computer); private: std::string CPU; }; computer::computer() { this->CPU = "i7"; } std::string computer:: description() { std::stringstream ret; ret << "CPU:" << CPU; return ret.str(); } void upgrade(computer *computer) { computer->CPU = "i9"; } int main() { computer com; std::cout << com.description() << std::endl; upgrade(&com); std::cout<<com.description()<<std::endl; system("pause"); return 0; }
使用类的成员函数作为友元函数
这里再写函数时遇到几个问题,因为为了方便复制过来,我没有把类和方法以及main函数分开写,不知道问题是不是出现在这
1.先定义的类的成员函数才可以在后定义的类里 作为成员函数
2.因为上面那点,所以先定义的类的成员函数可能会引用下面的类,所以要在前面先申明,比如我写的 class computer,如果涉及到具体方法还要添加头文件
#include <iostream> #include <string> #include <sstream> //把类的成员函数设为友元函数 //设一个升级CPU的函数 class computer; class computerServe { public: void upgrade(computer* computer); private: std::string CPU; }; class computer { public: computer(); std::string description(); friend void computerServe::upgrade(computer* computer); private: std::string CPU; }; computer::computer() { this->CPU = "i7"; } std::string computer:: description() { std::stringstream ret; ret << "CPU:" << CPU; return ret.str(); } void computerServe::upgrade(computer *computer) { computer->CPU = "i9"; } int main() { computer com; computerServe serve; std::cout << com.description() << std::endl; serve.upgrade(&com); std::cout<<com.description()<<std::endl; system("pause"); return 0; }
-
C++程序设计 友元函数、友元类 友元函数和友元类.ppt
2020-09-16 07:26:34xxx xxxxx 目 录 页 xx C++语言程序设计 xxx xxxxx 目 录 页 C++语言程序设计 绪 论 大连理工大学出版社 地址大连市软件园路80号 E-mail : dutp@ URLhttp://www.dutp,cn 友元函数和友元类 友元函数和友元类 类具有...
收藏数
10,318
精华内容
4,127
-
Qt--动效界面截图
-
moloch-2.3.0-1.x86_64.rpm
-
机器人对话的交互设计原则
-
ELF视频教程
-
Wetool多开工具.exe
-
智能停车场云平台(附vue+SpringBoot前后端项目源码)
-
python数据处理之两个一维数组合成2维数组
-
2021 PHP租车系统 毕业设计 毕设源码 源代码使用教程
-
《Unix/Linux编程实践教程》chapter5 连接控制:学习stty
-
ctfshow web入门 序列化
-
实施情感检测分类器-源码
-
集成学习之bagging、boosting及AdaBoost的实现
-
Java异步非阻塞编程的几种方式
-
javaChatRoom(jb51.net).rar
-
80
-
Mysql数据库面试直通车
-
边缘和对比度增强的NSST域红外与可见光图像融合
-
商店角度-源码
-
Maven配置阿里云镜像仓库
-
cluster-hadoop-plugin.sh