目录
11.只能采用成员函数方式不能采用友元函数方式实现的运算符有哪些?为什么?
12.只能采用友元函数方式而不能采用成员函数方式实现的运算符有哪些?为什么?
13.说明类成员函数实现运算符重载与普通函数实现运算符重载的区别?
15.对于非C++内建类型A和B,有哪几种方法可以将B的对象转换成A的对象?
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为例:
- 通过在类B中设计相应的转换构造函数进行转换:B(const A & ){//进行转换}
- 通过在类A中设计重载类型转换运算符()成员函数进行转换:operator B(){//进行转换}
- 通过类指针实现强制转换:A a; B* pb=(B*)(&a); //这种转换方式实际上是两个不同类对象的各数据成员的按字节赋值转换。
15.对于非C++内建类型A和B,有哪几种方法可以将B的对象转换成A的对象?
- class B:public A{…}; //B公有继承自A,可以实现间接转换;
- class B{ operator A(){…};}; //在类B中设计类型转换重载成员函数,实现隐式转换;
- class A{A(const B&){…};}; //在A中设计转换构造函数,可以实现非隐式转换;
- A & operator=(const B &){…}; //在A中设计重载赋值运算符函数,通过赋值语句实现转换。
16.简要说明C++运算符重载的注意事项。
- 一般情况下,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。
- 有些双目运算符不能重载为类的友元函数,例如=、()、[]、->
- 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
- 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选友元函数。
- 当重载运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。
参考:《直击招聘 程序员面试笔试 C++语言深度解析》李春葆、李筱池 主编