面试题答案
一键面试合理使用友元关系提升效率和灵活性的场景
- 运算符重载:
- 例如,实现复数类
Complex
,对于+
运算符,如果使用成员函数重载,Complex
对象只能在左侧,不太符合数学使用习惯。使用友元函数重载+
运算符,可以更自然地实现复数加法。
class Complex { private: double real; double imag; public: Complex(double r = 0, double i = 0) : real(r), imag(i) {} // 友元函数声明 friend Complex operator+(const Complex& a, const Complex& b); }; Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); }
- 这里友元函数可以直接访问
Complex
类的私有成员,方便地实现运算符功能,且没有严重破坏封装性,因为+
运算符本质上是对复数对象数据的一种操作,与类关系紧密。
- 例如,实现复数类
- 辅助类访问:
- 假设有一个
Point
类和一个Line
类,Line
类用于表示由两个Point
组成的线段。Line
类可能需要直接访问Point
类的坐标值(私有成员)来进行一些计算,如计算线段长度。
class Point { private: double x; double y; public: Point(double a = 0, double b = 0) : x(a), y(b) {} // 声明Line类为友元 friend class Line; }; class Line { private: Point start; Point end; public: Line(const Point& s, const Point& e) : start(s), end(e) {} double length() { double dx = end.x - start.x; double dy = end.y - start.y; return std::sqrt(dx * dx + dy * dy); } };
Line
类作为Point
类的友元,能直接访问Point
类的私有成员,使得Line
类的实现更简洁高效,同时也没有过度暴露Point
类的实现细节,因为Line
类与Point
类在业务逻辑上紧密相关。
- 假设有一个
减少友元关系负面影响的设计模式手段
- 桥接模式:
- 例如,有一个图形绘制系统,存在不同的图形类(如圆形、矩形)和不同的绘制平台(如Windows、Linux)。可以使用桥接模式将图形类和绘制平台分离。假设图形类有一些私有数据用于表示图形属性,绘制平台类需要访问这些属性来进行绘制。
- 传统方式可能会使用友元关系让绘制平台类访问图形类的私有成员。但使用桥接模式,通过定义抽象接口和实现接口的方式,避免了友元关系。
- 抽象图形类:
class DrawAPI { public: virtual void drawCircle(double x, double y, double radius) = 0; }; class Shape { protected: DrawAPI* drawAPI; public: Shape(DrawAPI* api) : drawAPI(api) {} virtual void draw() = 0; };
- 具体图形类(以圆形为例):
class Circle : public Shape { private: double x, y, radius; public: Circle(double a, double b, double r, DrawAPI* api) : Shape(api), x(a), y(b), radius(r) {} void draw() override { drawAPI->drawCircle(x, y, radius); } };
- 具体绘制平台类(以Windows绘制平台为例):
class WindowsDrawAPI : public DrawAPI { public: void drawCircle(double x, double y, double radius) override { // Windows 特定的绘制代码 std::cout << "Drawing Circle at (" << x << ", " << y << ") with radius " << radius << " on Windows" << std::endl; } };
- 这里通过桥接模式,
DrawAPI
实现类不需要成为Shape
类的友元,就可以访问绘制所需的参数,符合开闭原则(可以轻松添加新的图形或绘制平台)和依赖倒置原则(依赖抽象而非具体实现)。
- 代理模式:
- 假设一个敏感数据类
SensitiveData
,有一些外部模块需要访问其部分功能,但不能直接访问其私有成员。可以创建一个代理类DataProxy
。 SensitiveData
类:
class SensitiveData { private: int secretValue; public: SensitiveData(int value) : secretValue(value) {} int getSecretValue() { return secretValue; } };
DataProxy
类:
class DataProxy { private: SensitiveData* data; public: DataProxy(SensitiveData* d) : data(d) {} int accessSecretValue() { // 可以在这里添加额外的逻辑,如权限检查 return data->getSecretValue(); } };
- 外部模块通过
DataProxy
访问SensitiveData
,避免了直接让外部模块成为SensitiveData
的友元,符合开闭原则(可以在代理类中添加新的功能)和依赖倒置原则(依赖SensitiveData
的抽象接口,如getSecretValue
函数)。
- 假设一个敏感数据类