第四章 《友元和运算符重载》

目录

1.什么是友元函数?友元函数有什么优缺点?

2.友元函数有什么特点?

3.定义友元函数时要注意什么?

4.什么是友元类?

5.友元关系有什么特点?

6.什么是运算符重载?运算符重载有哪些规定?

7.哪些运算符不能重载?为什么?

8.简述运算符重载的意义?

9.运算符重载有哪几种方式?

10.具体的运算符重载?

11.只能采用成员函数方式不能采用友元函数方式实现的运算符有哪些?为什么?

12.只能采用友元函数方式而不能采用成员函数方式实现的运算符有哪些?为什么?

13.说明类成员函数实现运算符重载与普通函数实现运算符重载的区别?

14.两个没有继承关系的类对象之间可以转换吗?有几种方式?

15.对于非C++内建类型A和B,有哪几种方法可以将B的对象转换成A的对象?

16.简要说明C++运算符重载的注意事项。


1.什么是友元函数?友元函数有什么优缺点?

友元函数指某些虽然不是类成员函数却能够通过类对象访问其所有成员的函数。类授予它的友元特别的访问权。

使用友元函数的优点是提高编程的灵活性和程序执行效率,缺点是破坏了类的封装机制。


2.友元函数有什么特点?

友元函数不是类的成员函数,但需要在类中声明。

若在类A中声明了友元函数fun(),fun()不能直接访问类A的成员,只能通过类A的对象a访问其所有的成员,包括私有和保护的成员。

由于友元函数不是类的成员函数,它没有隐含的this指针。

友元函数的调用方式和普通函数一样,不需要通过对象来调用。

实际上一个友元函数还可以作为另外类的成员函数,在这种情况下,它既有友元函数的特点,又有成员函数的特点。

由于友元函数可以通过类对象访问其所有成员,破坏了数据的安全性,所以使用友元函数必须谨慎,不要通过友元函数对数据成员进行危险的操作。


3.定义友元函数时要注意什么?

友元函数本质上是全局的,其声明位置可以放在类的任何地方,既可以在public区,也可以在protected或private区,意义完全一样。友元函数不是类的成员,所以友元函数是不能继承的,这也是友元函数不需要考虑访问权限的原因。

从原则上讲,友元函数在类里面声明,在类外实现。但是如果友元函数有该类类型的对象参数,可以在类里面实现。

当一个类的成员函数作为另一个类的友元函数时必须先定义成员函数所在的类,在声明友元函数时要加上成员函数所在类的类名和作用域运算符::。


4.什么是友元类?

和将一个函数设计为一个类的友元函数一样,可以将一个类声明为另一个类的友元类。若B类是A类的友元类,则B类的所有成员函数都是A类的友元函数。


5.友元关系有什么特点?

友元关系是非传递的,例如类B是类A的友元类,类C是类B的友元类,在类C和类A之间如果没有声明就没有任何友元关系。

友元关系是单向的,如果声明类B是类A的友元类,类B的成员函数就可以通过对象访问类A的私有和保护数据成员,但类A的成员函数不能访问类B的私有和保护数据成员。

友元关系是不能继承的,如函数f()是类A的友元函数,类A派生出类B,函数f()并不是类B的友元函数,除非在类B中作了特殊声明。


6.什么是运算符重载?运算符重载有哪些规定?

运算符重载就是用同一个运算符完成不同的运算功能。

和函数重载一样,运算符重载也是在编译阶段完成的,体现出静态的多态性。

C++运算符重载的相关规定如下:

不能改变原运算符的优先级和结合性。

默认参数不能和运算符重载一起使用,也就是说在设计运算符重载时不能使用默认参数。

不能改变原运算符的操作数个数。

不能创建新的运算符,只有已有运算符可以被重载。

当运算符作用于C++内部提供的数据类型时,原来的含义保持不变。

运算符可以被重载用于用户定义的类对象或者用户定义的类对象与内置数据类型变量的组合。

7.哪些运算符不能重载?为什么?

运算符说明不能重载的原因
.成员运算符为保证成员运算符对成员访问的安全性,故不允许重载
.*成员指针运算符同上
::作用域运算符左边的运算数是类型名,而不是表达式
?:三目运算符在C++中没有定义三目运算符重载的语法
sizeof求大小运算符其运算数是一个类型名,而不是一个表达式

8.简述运算符重载的意义?

为了使用户自定义数据类型的数据的操作与内置数据类型的数据的操作形式一致。


9.运算符重载有哪几种方式?

可以采用普通函数、友元函数或者类成员函数来实现。

一般来讲,单目运算符最好重载为成员函数,双目运算符最好重载为友元函数。


10.具体的运算符重载?

重载++、--单目运算符

//采用成员函数重载格式:
函数类型 operator ++();                 //重载前缀运算符
函数类型 operator ++(int);              //重载后缀运算符

//采用友元函数重载格式:
friend 函数类型 operator ++(A &);       //重载前缀运算符
friend 函数类型 operator ++(A &,int);   //重载后缀运算符

重载比较运算符==、>、<等

//采用成员函数重载格式:
bool operator ==(const A &);
//采用友元函数重载格式:
friend bool operator==(const A &,const A &);

重载赋值运算符+=、-=、=

//+=、-=这些可以采用两种方式,=只能重载为成员函数
//成员函数方式:
A &operator=(A &);
//友元函数方式:
friend A & operator+=(A &,const A &);

重载下标运算符[]

//成员函数方式:
函数类型 & operator[](int n);

重载new、delete、new[]、delete[]

new和delete只能被重载为类的成员函数或者普通函数,不能被重载为友元函数,而且不论是否使用关键字static进行修饰,重载的new、delete均为类的静态成员函数。

//重载为成员函数:
void *operator new(size_t size,其它形参);
void operator delete(void *p);

重载类型转换运算符

C++中提供了标准类型的相互转换,如“int n=(int)1.87;”

同样可以进行这种类型转换运算符重载:

operator 类型名()
{
    函数体;
}

与前面的重载运算符函数不同的是,类型转换运算符重载函数没有返回类型,因为“类型名”就代表了它的返回类型,而且没有任何参数。在调用过程中要带一个对象实参。 

重载函数调用运算符()

重载函数调用运算符()之后,允许将类对象像函数一样使用。与其他重载运算符不同的是,函数调用运算符可以有任意个参数,因此可以定义多个不同的函数版本的函数调用运算符重载。

//成员函数方式:
函数类型 operator()(形参);

重载输入输出运算符<<、>>

//友元函数方式:
friend ostream & operator <<(ostream & stream,类名 & 类引用名)   //输出
{
    函数体;
    return stream;
}
//显式调用:cout<<对象
//隐式调用:operator<<(cout,对象)

friend istream & operator >>(istream & stream,类名 & 类引用名)    //输入
{
    函数体;
    return stream;
}

 


11.只能采用成员函数方式不能采用友元函数方式实现的运算符有哪些?为什么?

有=、[]、()、->和new/delete(new[]/delete[])

=:在任何类中,如果没有显式定义重载=运算符,编译器会默认提供一个,如果采用友元方式重载=,编译器又定义一个默认的=运算符函数(在类里面),那么在对象赋值时使用哪一个呢?这样会造成调用的二义性。

[]:假设已定义了类A,若有“A a[10];”,编译器会提供默认的[]运算符。如果采用友元方式重载[],当遇到形如a[1]的表达式时编译器也会产生调用的二义性。

():如果采用友元方式重载(),当遇到形如a(1,2)的表达式时是调用构造函数还是重载()运算符呢?编译器会产生调用的二义性。

->:假设已定义了类A,若有A *pa,编译器会提供默认的->运算符。如果采用友元方式重载->运算符,当遇到形如pa->f()的表达式时编译器会产生调用的二义性。

new/delete:这对运算符比较特殊,有固定的使用格式,如new运算符的返回类型必须是void *,且第一个参数必须是unsigned int类型,而使用友元方式实现重载时第一个参数应该是类对象,所以无法采用友元方式重载。

实际上,运算符=、()、[]、->是程序中最常用的符号,尽管可以通过上下文的判断消除二义性,但增加了编译器的复杂性,所以C++规定不允许这些运算符采用友元函数方式重载,简化编译器设计。


12.只能采用友元函数方式而不能采用成员函数方式实现的运算符有哪些?为什么?

只有<</>>。由于<<和>>运算符都不是通过对象调用的,所以它们不能采用类成员函数实现重载,因为调用类成员函数时第一个参数必须是类的对象,而<<和>>的第一个参数是流对象引用,所以只能采用友元方式重载。


13.说明类成员函数实现运算符重载与普通函数实现运算符重载的区别?

两者的区别主要是函数参数的个数不同,采用类成员函数实现运算符重载时隐含当前对象,所以比普通函数实现运算符重载的参数个数少一个。


14.两个没有继承关系的类对象之间可以转换吗?有几种方式?

可以转换,有三种方式。以将A类对象a转换成B类对象b为例:

  1. 通过在类B中设计相应的转换构造函数进行转换:B(const A & ){//进行转换}
  2. 通过在类A中设计重载类型转换运算符()成员函数进行转换:operator B(){//进行转换}
  3. 通过类指针实现强制转换:A a;  B* pb=(B*)(&a);                 //这种转换方式实际上是两个不同类对象的各数据成员的按字节赋值转换。

15.对于非C++内建类型A和B,有哪几种方法可以将B的对象转换成A的对象?

  1. class B:public A{…};    //B公有继承自A,可以实现间接转换;
  2. class B{ operator A(){…};}; //在类B中设计类型转换重载成员函数,实现隐式转换;
  3. class A{A(const B&){…};};    //在A中设计转换构造函数,可以实现非隐式转换;
  4. A & operator=(const B &){…};  //在A中设计重载赋值运算符函数,通过赋值语句实现转换。

16.简要说明C++运算符重载的注意事项。

  1. 一般情况下,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。
  2. 有些双目运算符不能重载为类的友元函数,例如=、()、[]、->
  3. 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
  4. 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选友元函数。
  5. 当重载运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。

参考:《直击招聘 程序员面试笔试 C++语言深度解析》李春葆、李筱池 主编

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值