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

先看这样一个例子:

#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;
}

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

必须存在继承关系;

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

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

C++ 虚函数和友元的更多相关文章

  1. c++虚函数注意事项

    >在基类方法声明中使用关键字virtual,可以使该方法在基类及所有的派生类中是虚的 >如果使用指向对象的引用或指针来调用虚方法,程序将使用对象类型定义的方法,而不使用为引用或指针类型定义 ...

  2. C++中虚函数的作用和虚函数的工作原理

    1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...

  3. C++中的多态及虚函数大总结

    多态是C++中很关键的一部分,在面向对象程序设计中的作用尤为突出,其含义是具有多种形式或形态的情形,简单来说,多态:向不同对象发送同一个消息,不同的对象在接收时会产生不同的行为.即用一个函数名可以调用 ...

  4. <C++>友元与虚函数的组合

    为类重载<<与>>这两个运算符时,重载函数必须为该类的友元函数. 当友元不能被继承,故不能当作虚函数,无法使用多态. 可以用以下结构实现友元与虚函数的组合. class bas ...

  5. C++ 系列:虚函数

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  6. C++虚方法(虚函数)随笔

    本文不讨论虚函数的原理,只简单总结下虚函数的常用事项. 虚函数(虚方法)是C++动态联编 实现多态的重要手段,在函数声明时使用关键字virtual即可,如: virtual void func(voi ...

  7. C++ - 虚基类、虚函数与纯虚函数

    虚基类       在说明其作用前先看一段代码 class A{public:    int iValue;}; class B:public A{public:    void bPrintf(){ ...

  8. C++中不能声明为虚函数的有哪些函数

    常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload,不能被o ...

  9. C++中的抽象类及纯虚函数的实现与否

    1.含有纯虚函数的叫抽象类 2.抽象类(一般是基类)中的纯虚函数无论函数体实现与否,都没有关系,系统会自动忽略 3.继承自抽象类的子类,必须要实现父类的纯虚函数才可以实例化对象 4.抽象类不允许实例化 ...

随机推荐

  1. 【Reverse】DLL注入

    DLL注入就是将dll粘贴到指定的进程空间中,通过dll状态触发目标事件 DLL注入的大概流程 https://uploader.shimo.im/f/CXFwwkEH6FPM0rtT.png!thu ...

  2. Excel 数据验证:分类选择及输入限制

    几个简单设置让你的数据不再出错 如何快速选择某一大类中的细分小类 多级菜单 注意:引用可以创建二级目录,但是引用前应先用公式定义名称,然后引用,引用只能在本sheet操作.

  3. 字节数与字符数mysql_mysql里一个中文汉字占多少字节数?

    在mysql中,如果是latin1字符集下,一个中文汉字占2个字节数:如果是utf8字符集下,一个中文汉字占3个字节数:如果是gbk字符集下,一个中文汉字占2个字节数. mysql各字符集下汉字和字母 ...

  4. layui 弹窗中 分页展示table

    1. 需求:点击查看更多,展示该类别 所有数据,并分页 2. 参考文档: (1)https://www.jianshu.com/p/40da11ebae66 (2) https://blog.csdn ...

  5. 【Linux】【Services】【SaaS】Docker+kubernetes(13. 部署Jenkins/Maven实现代码自动化发布)

    1. 简介 Jenkins: 官方网站:https://jenkins.io/ 下载地址:https://jenkins.io/download/ war包下载:http://mirrors.jenk ...

  6. my40_MySQL锁概述之意向锁

    本文在锁概述的基础上,通常实验举例,详细地介绍了意向锁的原理. 锁范围  全局锁(global lock)表锁(table lock)行锁 (row lock) ROW LOCK的粒度LOCK_REC ...

  7. 注册页面css版本

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  8. 智龙开发板搭建llsp环境

    智龙开发板搭建llsp(linux+lighttpd+sqlite3+php)环境 1. 准备 1. 智龙开发板V3 2. 软件编译环境:VirtualBox6+CentOS6.10-i386.min ...

  9. 启动Springboot 报错 Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback. Sat Jan 12 15:50:25 CST 2019 There was an unexpected error (type=Not

    解决方案:http://www.cnblogs.com/michaelShao/p/6675186.html

  10. 转:addChildViewController实现网易新闻首页切换

    本来只是打算介绍一下addChildViewController这个方法的,正好今天朋友去换工作面试问到网易新闻标签栏效果的实现,就结合它,用个小Demo实例介绍一下:(具体解释都写在了Demo里面的 ...