一、 友元(Friend)相关
1. 友元有什么作用?
- 突破封装性:在C++中,类的私有(private)和保护(protected)成员只能被类自身的成员函数访问。友元提供了一种机制,允许外部的函数或另一个类访问这些私有成员。
- 提高效率:在某些频繁访问私有成员的场景下,使用友元可以避免频繁调用公共的
get/set接口,从而减少函数调用的开销,提高程序运行效率。 - 方便运算符重载:在重载某些运算符(如
<<、>>或需要隐式转换左操作数的二元运算符)时,通常需要将其声明为友元函数,以便直接访问类的私有数据。
2. 友元有哪些形式?
- 友元函数(普通函数):在类中声明,但定义在类外部的普通函数。
- 友元类:将一个类(例如类B)声明为另一个类(类A)的友元,那么类B的所有成员函数都可以访问类A的私有和保护成员。
- 友元成员函数:只将另一个类中的某一个特定成员函数声明为友元,而不是整个类。
- 注意:友元关系是单向的(A是B的友元,不代表B是A的友元),且不能被继承,也不具有传递性(A是B的友元,B是C的友元,不代表A是C的友元)。
二、 运算符重载(Operator Overloading)相关
3. 哪些运算符能重载?哪些不能?
- 能重载的运算符:绝大多数运算符都可以重载。包括算术运算符(
+ - * / %)、关系运算符(== != < >)、逻辑运算符(&& || !)、位运算符(& | ^ ~ << >>)、赋值运算符(= += -=)、下标运算符([])、函数调用运算符(()等。 - 不能重载的运算符(共5个):
.(成员访问运算符).*(成员指针访问运算符)::(作用域解析运算符)?:(三目条件运算符)sizeof(长度运算符)
原因:这些运算符如果重载会引发严重的语义歧义,或者破坏C++的基础语法机制。
4. 运算符重载的本质是什么?
- 运算符重载的本质是函数重载。当编译器遇到被重载的运算符时,它会将其转换为对特定函数的调用。这个函数的名字是由
operator关键字加上要重载的运算符符号组成的(例如:operator+、operator<<)。它同样是遵循C++函数重载的规则(参数列表不同,实现不同)。
5. 运算符重载的形式有哪些?
- 类的成员函数形式:作为类的非静态成员函数重载。此时左操作数隐含为当前对象(
this指针)。 - 类的非成员函数(通常为友元函数)形式:定义在类外部。如果是普通函数,可能需要通过公有接口访问私有成员;如果声明为友元函数,则可以直接访问私有成员。左操作数必须作为函数的第一个参数显式传递。
6. 什么情况下才需要进行运算符重载?只有内置类型可以重载吗?
- 什么时候需要:当我们需要让**自定义类型(类/结构体)**的对象,像内置类型一样进行直观的操作时。例如:两个复数对象相加(
c1 + c2),或者两个字符串对象拼接。重载的目的是为了让代码更直观、更易读。 - 只有内置类型可以重载吗? 不是。 恰恰相反,内置类型(如
int,double)的运算符不能重载。运算符重载必须是针对自定义类型的,或者重载的运算符中至少有一个操作数是自定义类型(类、结构体或枚举)。如果试图对两个内置类型重载(如重定义int + int),编译器会报错。
7. 运算符重载时要考虑哪些因素?
- 语义一致性:重载后的运算符功能应当符合人们的直觉,不要重载
+去实现减法,否则代码极其难以维护。 - 是否作为成员函数:C++规定,赋值运算符
=、下标运算符[]、函数调用运算符()、成员访问箭头->必须作为成员函数重载。其他运算符则灵活选择。 - 返回值类型:要考虑是否返回引用(如
+=通常返回引用以支持链式操作a += b += c),是否返回const对象(防止(a + b) = c这种非法操作)。 - 友元与成员函数的选择:对于需要隐式转换第一个操作数的情况(例如
2 + complexObj),通常需要将运算符重载为非成员函数(往往配合友元);如果修改的是自身对象状态(如+=),通常作为成员函数更合适。 - 不要过度重载:重载运算符应当保持代码的清晰度,如果逻辑复杂到难以理解,不如定义一个具有明确命名的普通成员函数(如
add()、concat())。 - 效率和异常安全:在实现过程中,注意对象的拷贝开销,尽量使用常量引用传递参数,并考虑异常安全的处理。