Limesn
Limesn
发布于 2026-09-18 / 5 阅读
1

第五章 运算符重载

一、 友元(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个)
    1. . (成员访问运算符)
    2. .* (成员指针访问运算符)
    3. :: (作用域解析运算符)
    4. ?: (三目条件运算符)
    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())。
  • 效率和异常安全:在实现过程中,注意对象的拷贝开销,尽量使用常量引用传递参数,并考虑异常安全的处理。