C++学习笔记46——友元与继承

来源:互联网 发布:鼎域名邸房价走势 编辑:程序博客网 时间:2024/05/18 03:26

1,总的原则

继承层次中关于友元的总原则就是:友元关系不能继承。可以概述如下:
  1. 老爸的朋友不一定是我的朋友;
  2. 朋友的儿子不一定还是朋友;
  3. 由以上两条不难推出:老爸的朋友的儿子不一定还是朋友
但具体实施起来情况更为复杂,下面分别验证。

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);// OKfrd.check(s);// 居然也OK,使用基类的地方可以使用派生类frd.check2(s);// 居然还OK!}
输出如下:


应当注意几点:
  1. 类Frnd的定义体内不能使用s.son_test()证明了基类的友元不再是派生类的友元;
  2. check2的定义体证明,派生类中从基类继承过来的成员还是对友元开放的
  3. 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);// OKfrd.check(s);// 居然也OK}
输出结果:

这段代码最让人困惑的地方在于:Frnd中check2定义失败是由于它需要使用Son2::test(),但是在主函数中frd.check(s)借由动态绑定事实上也使用了Son2::test()——这一点可以由输出打印结果确认——但是却运行正确。就好像是用动态绑定骗过了编译器一样。
我们将友元与virtual函数的关系总结如下:
  1. 我们知道派生类中的成员分为3类:直接继承自基类的成员、重定义的虚函数和自己新增的成员;
  2. 基类的友元函数可以使用直接继承自基类的成员,不可以使用派生类新增的成员
  3. 对于派生类重定义的虚函数,基类的友元不能通过派生类的对象直接使用,因为这样必然导致使用的是派生类中的版本;但是可以通过动态绑定使用:即友元名义上用的是指向基类的指针或引用,但实际上该指针或引用指向了派生类的对象,友元中可以使用这种方法使用派生类的virtual成员,别且最终用到的还是派生类中的版本。
由以上3条我们可以推测:如果改变条件将动态绑定取消,比如讲check()改为通过值传递的版本,则check直接使用基类中的版本:
//其他地方不变,将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);}
输出结果与上一节一模一样:


总结如下:
  1. FrndSon中不能使用Baba中的protected成员,证明Frnd对Baba的友元关系没有继承给Frnd的派生类FrndSon;
  2. FrndSon的对象还是可以使用继承自Frnd的public成员函数check,而check()是Baba的友元,这种友元关系不变。
脑洞开一下,由于FrndSon是Frnd的派生类,那么FrndSon中的成员同样可以分成3类:直接继承自基类的成员、重定义的虚函数和自己新增的成员。直接继承自基类的成员(check)仍然是友元,自己新增的成员(son_check)不是友元已经验证过了。那么重定义的虚函数还是不是友元呢?
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成员。

0 0