面试题答案
一键面试友元函数和虚函数本质特性
- 友元函数:
- 友元函数是一种特殊的函数,它不属于类的成员函数,但能访问类的私有和保护成员。它破坏了类的封装性,为外部函数提供了访问类内部成员的权限。友元函数没有
this
指针,因为它不是类的成员函数。 - 友元关系是单向且不能传递的,即如果类
A
将函数f
声明为友元,并不意味着f
对其他类也有同样的访问权限,且类B
是类A
的友元,类C
是类B
的友元,不代表类C
是类A
的友元。
- 友元函数是一种特殊的函数,它不属于类的成员函数,但能访问类的私有和保护成员。它破坏了类的封装性,为外部函数提供了访问类内部成员的权限。友元函数没有
- 虚函数:
- 虚函数是类的成员函数,在基类中使用
virtual
关键字声明。其目的是实现运行时多态性,即根据对象的实际类型(而不是指针或引用的静态类型)来决定调用哪个函数版本。 - 虚函数依赖于对象的虚函数表(vtable),通过对象的指针或引用调用虚函数时,会根据对象的vtable找到实际要调用的函数地址。每个包含虚函数的类都有一个vtable,对象中包含一个指向该vtable的指针(vptr)。
- 虚函数是类的成员函数,在基类中使用
两者冲突导致友元函数无法声明为虚函数的原因
- 所属范畴不同:
- 友元函数不属于类的成员,而虚函数是类的成员函数。虚函数机制依赖于对象的vtable,而友元函数没有
this
指针,不属于类的成员体系,无法参与基于类成员的虚函数机制。
- 友元函数不属于类的成员,而虚函数是类的成员函数。虚函数机制依赖于对象的vtable,而友元函数没有
- 调用方式不同:
- 虚函数是通过对象的指针或引用进行动态绑定调用的,根据对象的实际类型决定调用的函数版本。友元函数的调用与普通函数类似,在编译时就确定了调用关系,不依赖对象的动态类型,与虚函数的动态绑定机制不兼容。
假设友元函数可以是虚函数带来的复杂性和潜在问题
- 复杂性:
- 打破现有机制:会打破C++中友元函数和虚函数现有的清晰界限和各自独立的实现机制。例如,编译器需要重新设计处理友元函数的虚函数表,这会增加编译的复杂性。
- 增加继承体系复杂性:在继承体系中,虚友元函数的行为会变得难以理解和预测。因为友元关系是单向且不传递的,而虚函数具有多态性和可继承性,这两种特性结合会导致复杂的语义。比如,子类可能会意外地改变基类友元函数的虚函数行为,使得代码的可读性和维护性降低。
- 潜在问题:
- 破坏封装性:本来友元函数就已经破坏了类的封装性,若其成为虚函数,在多态调用过程中,可能会导致更多意外的访问权限问题。例如,通过基类指针调用虚友元函数时,可能会访问到子类中原本不应该被外部访问的私有成员,进一步扩大了封装性的破坏范围。
- 运行时错误:由于友元函数的调用不依赖对象的动态类型,若将其变为虚函数,在运行时动态绑定可能会出现错误。比如,当友元函数需要访问对象的特定状态,但该状态在不同子类中有不同实现时,虚友元函数可能无法正确处理,导致运行时错误。