1,总的原则
继承层次中关于友元的总原则就是:友元关系不能继承。可以概述如下:
- 老爸的朋友不一定是我的朋友;
- 朋友的儿子不一定还是朋友;
- 由以上两条不难推出:老爸的朋友的儿子不一定还是朋友
2,验证派生类不能继承基类的友元
// 类的定义
struct Frnd;//类的前置声明
class Baba
{
public:
friend Frnd;//友元声明,还记得吗?
protected:
void test() { cout << "I am your father!\n" << endl; }
};
class Son2: public Baba
{
private:
void son_test() { cout << "I am your son!\n" << endl; }
};
struct Frnd
{
//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
void check(Baba &f) { f.test(); }
void check2(Son2 &s) { s.test(); }
//void check3(Son2 &s) { s.son_test(); }//error C2248: “Son2::son_test”: 无法访问 private 成员(在“Son2”类中声明)
};
//主函数
int main()
{
Baba f;
Son2 s;
Frnd frd;
//1,验证派生类不能继承父类的友元
frd.check(f);// OK
frd.check(s);// 居然也OK,使用基类的地方可以使用派生类
frd.check2(s);// 居然还OK!
}
输出如下:
应当注意几点:
- 类Frnd的定义体内不能使用s.son_test()证明了基类的友元不再是派生类的友元;
- check2的定义体证明,派生类中从基类继承过来的成员还是对友元开放的;
- main函数中frd.check(s)的用法,是在需要基类的地方使用public派生类。
3,验证友元、继承与动态绑定
// 结构体定义
struct Frnd;
class Baba
{
public:
friend Frnd;
protected:
virtual void test() { cout << "I am your father!\n" << endl; }
};
class Son2 : public Baba
{
private:
virtual void test() { Baba::test(); cout << "son again!\n"; }
void son_test() { cout << "I am your son!\n" << endl; }
};
struct Frnd
{
//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
void check(Baba &f) { f.test(); }
//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};
//主函数
int main()
{
Baba f;
Son2 s;
Frnd frd;
frd.check(f);// OK
frd.check(s);// 居然也OK
}
输出结果:
这段代码最让人困惑的地方在于:Frnd中check2定义失败是由于它需要使用Son2::test(),但是在主函数中frd.check(s)借由动态绑定事实上也使用了Son2::test()——这一点可以由输出打印结果确认——但是却运行正确。就好像是用动态绑定骗过了编译器一样。
我们将友元与virtual函数的关系总结如下:
- 我们知道派生类中的成员分为3类:直接继承自基类的成员、重定义的虚函数和自己新增的成员;
- 基类的友元函数可以使用直接继承自基类的成员,不可以使用派生类新增的成员
- 对于派生类重定义的虚函数,基类的友元不能通过派生类的对象直接使用,因为这样必然导致使用的是派生类中的版本;但是可以通过动态绑定使用:即友元名义上用的是指向基类的指针或引用,但实际上该指针或引用指向了派生类的对象,友元中可以使用这种方法使用派生类的virtual成员,别且最终用到的还是派生类中的版本。
//其他地方不变,将check改为值传递版本
struct Frnd
{
//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
void check(Baba f) { f.test(); }
//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};
则输出结果如下:
两次使用的都是基类中的版本。
4,验证友元的派生类不再是友元
其他代码不变,增加如下定义:
//3,验证友元的派生类不再是友元
struct FrndSon:public Frnd
{
//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
//void son_check(Baba &f) { f.test(); }
};
主函数如下实现:
int main()
{
Baba f;
Son2 s;
FrndSon fs;
fs.check(f);
fs.check(s);
}
输出结果与上一节一模一样:
总结如下:
- FrndSon中不能使用Baba中的protected成员,证明Frnd对Baba的友元关系没有继承给Frnd的派生类FrndSon;
- FrndSon的对象还是可以使用继承自Frnd的public成员函数check,而check()是Baba的友元,这种友元关系不变。
struct Frnd
{
//友元可以使用派生类从基类继承的成员,但不能使用派生类自己新定义的成员
void check(Baba &f) { f.test(); }
virtual void v_check(Baba &f) { f.test(); }
//void check2(Son2 &s) { s.test(); }//error C2248: “Son2::test”: 无法访问 private 成员(在“Son2”类中声明)
};
//3,验证友元的派生类不再是友元
struct FrndSon:public Frnd
{
//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
//virtual void v_check(Baba &f) { f.test(); }
//error C2248: “Baba::test”: 无法访问 protected 成员(在“Baba”类中声明)
//void son_check(Baba &f) { f.test(); }
};
毫不意外,虚函数同样没有获得友元属性。
其实简单来说就是:友元的派生类(FrndSon)的类定义体中不能用到其他类(Baba)的protected或private成员。