虚函数具有动态联编性,在类族中有强大功能;友元函数具有跨类访问的功能,本质却是一种对封装的破坏。

先看这样一个例子:

#include<iostream>
using namespace std;
class A;
class B
{
private:
int x;
void print()
{
cout<<x<<endl;
}
public:
B(int i = 0)
{
x = i;
}
friend class A;
};
class A
{
public:
void func(B b)
{
b.print();
}
};
class D: public B
{
public:
D(int i):B(i) {}
};
int main()
{
cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(D)<<endl;
D d(99);
A a;
a.func(d);
return 0;
}

程序执行结果为:
        1 4 4
        99
上例中,A是B的友元类,A中的所有成员函数都为B的友元函数,可访问B的私有成员函数。友元类A大小为1,基类和派生类大小都是4,友元类A不是基类B的一部分,更不是派生类D的一部分。

 从上例看,友元似乎能够被继承,A的函数func这能访问B的派生类D嘛!这不基类的友元函数或友元类能够访问派生类的私有成员!

但若将上例中的继承关系改为私有继承,则:

   class D: private B
a.func(d); // error C2243: “类型转换”: 从“D *”到“const B &”的转换存在,但无法访问

我们知道:public继承是一种“is a”的关系,即一个派生类对象可看成一个基类对象。所以,上例中不是基类的友元被继承了,而是派生类被识别为基类了

再比如这样一个例子

#include<iostream>
using namespace std;
class B;
class A
{
private:
void print()
{
cout<<"A::print"<<endl;
}
public:
friend class B;
};
class B
{
public:
void func(A a)
{
a.print();
}
};
class D: public B { }; int main()
{
A a;
D d;
d.func(a);
return 0;
}

程序执行结果为:
        A::print
上例中,B为A的友元类,D是B的派生类,D继承了基类B的友元函数func,它能访问A的私有成员。由此可知一个友元类的派生类,可以通过其基类接口去访问设置其基类为友元类的类的私有成员,也就是说一个类的友元类的派生类,某种意义上还是其友元类
但若在上例D中新增加个成员函数,该函数是不能访问A私有成员的。

class D: public B
{
public:
void test(A a){ a.print(); } // error C2248: “A::print”: 无法访问 private 成员(在“A”类中声明)
};
#include<iostream>
using namespace std;
class A;
class B
{
private:
void print()
{
cout<<"B::print"<<endl;
}
public:
friend class A;
};
class A
{
public:
void func(B b)
{
b.print();
}
};
class D: public B
{
private:
void print()
{
cout<<"D::print"<<endl;
}
};
int main()
{
D d;
A a;
a.func(d);
return 0;
}

程序执行结果为:
    B::print
和前两例类似,友元关系并没有被继承,仅是派生类对象当成了一个基类对象来用,因此输出“B::print”。
若将上例print函数改为虚函数并通过多态来访问,就可以达到类似于友元可以继承的效果。

class A;
class B
{
private:
virtual void print()
{
cout<<"B::print"<<endl;
}
public:
friend class A;
};
class A
{
public:
void func(B* pb)
{
pb->print();
}
};
class D: public B
{
private:
virtual void print()
{
cout<<"D::print"<<endl;
}
}; int main()
{
D d;
A a;
a.func(&d);
return 0;
}

这本质上就是满足了多态的三个条件:

必须存在继承关系;

继承关系中必须有同名的虚函数,并且它们是覆盖关系。

存在基类的指针,通过该指针调用虚函数。

最新文章

  1. [LeetCode] Partition List 划分链表
  2. python 学习笔记十八 django深入学习三 分页,自定义标签,权限机制
  3. 【bzoj2333】 [SCOI2011]棘手的操作 可并堆+lazy标记
  4. php 地址跳转
  5. 关于shell脚本编程的10个最佳实践
  6. Netbeans源代码编辑技巧——使用代码补全和代码生成
  7. recursion 递归以及递归的缺点
  8. Angular - - $interval 和 $timeout
  9. 静态链表实现(A-B)+(B-A)【代码】
  10. SpringBoot的几个使用技巧
  11. 实验与作业(Python)-04 数据类型、数学函数与字符串
  12. 痞子衡嵌入式:开启NXP-MCUBootUtility工具的HAB加密功能 - CST(中英双语)
  13. lwip协议栈移植(1)
  14. CI-CD平台搭建过程整理
  15. JavaScript&amp;Date对象
  16. 莫烦scikit-learn学习自修第五天【训练模型的属性】
  17. Java - HashTable源码分析
  18. Xshell 5 免费版本安装过程
  19. 20155236范晨歌 Exp2后门原理与实践
  20. 新浪微博API使用初步介绍——解决回调地址的问题

热门文章

  1. Angular Service设计理念及使用
  2. vim一键整理代码命令
  3. android studio 生成aar和引用aar
  4. 常见排序——Java实现
  5. ORACLE DBMS_ROWID包详解
  6. Sentry 开发者贡献指南 - 前端(ReactJS生态)
  7. LuoguP7478 【A】StickSuger 题解
  8. myeclipse 给类与方法添加注解模板方法
  9. [Flink-源码分析]Blink SQL 回撤解密
  10. soui(1)之一个半透明的窗口