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

先看这样一个例子:

#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. day05 django框架之路由层

    day05 django框架之路由层 今日内容概要 简易版django请求声明周期流程图(重要) 路由匹配 无名有名分组 反向解析 无名有名解析 路由分发 名称空间 伪静态 虚拟环境 简易版djang ...

  2. Hive(九)【自定义函数】

    目录 自定义函数 编程步骤 案例 需求 1.创建工程 2.导入依赖 3.创建类 4.打jar包 5.上传hive所在服务器 6.将jar添加到hive的classpath 7.创建临时函数与开发好的j ...

  3. Sharding-JDBC 实现垂直分库水平分表

    1.需求分析

  4. Java Swing布局管理器GridBagLayout的使用示例 [转]

    GridBagLayout是java里面最重要的布局管理器之一,可以做出很复杂的布局,可以说GridBagLayout是必须要学好的的, GridBagLayout 类是一个灵活的布局管理器,它不要求 ...

  5. C++基本函数的调用优化(构造、拷贝构造、赋值)

    合理的函数可提升时间和空间的利用率 //Test1.h #include<iostream> using namespace std; struct ST { private: int a ...

  6. Linux学习 - 脚本安装包

    脚本安装包不是独立的软件包类型,常见安装的是源码包

  7. 【编程思想】【设计模式】【结构模式Structural】front_controller

    Python版 https://github.com/faif/python-patterns/blob/master/structural/front_controller.py #!/usr/bi ...

  8. 【Linux】【Services】【SaaS】Docker+kubernetes(7. 安装Docker私有镜像仓库)

    1. 简介 1.1. 自己做个私有镜像,方便上传和下载,我也在docker官网注册了一个账号,做好的镜像可以传上去 1.2. Redhat自带私有镜像的功能,需要安装包,这是howto: https: ...

  9. java异常处理中throws和throw的使用

    异常介绍: 运行时异常.非运行时异常 在编写可能会抛出异常的方法时,它们都必须声明为有异常. 一.throws关键字 1.声明方法可能抛出的异常: 2.写在方法名后面: 3.可声明抛出多个异常,异常名 ...

  10. 【JavaWeb】【Maven】001 下载与配置

    Maven下载与配置 Download Url:Maven – Download Apache Maven After downloading it, unpack it and configure ...