C++语言笔记系列之十八——虚函数(1)
1.C++中的多态
(1)多态性:同一个函数的调用能够进行不同的操作,函数重载是实现多态的一种手段。
(2)联编:在编译阶段进行联接。即是在编译阶段将一个函数的调用点和函数的定义点联接起来。
A.静态联编:在编译阶段就完毕的函数联编——函数重载。
B.动态联编:在程序的执行阶段由系统自己主动选择详细的函数——虚函数。
注:C++的多态主要指的就是动态联编。
2.虚函数
(1)虚函数是在函数的定义时将其声明为虚函数就可以。
(2)说明:virtual 数据类型 函数名(參数表) {函数体}
A.目的:当通过基类指针调用虚函数时,系统进行动态联编。
B.在派生类中定义了和基类名称同样、參数个数同样、參数类型同样、返回值类型同样的函数时。若基类中的同名成员函数是虚函数,派生类的同名成员函数将自己主动被虚化。
C.通过对象訪问虚函数时採用静态联编。
动态联编仅仅可以通过指向对象的指针和对象的引用来完毕。
3.样例
example 1
#include <iostream.h>
class Point(
{
private:
float x, y;
public:
void setpoint(float i, float j)
{
x = i; y = j;
}
float area() {return 0.0;}//等价于:float area()=0;
};
const float PI=3.1416;
class Circle:pulic Point
{
private:
float rad;
public:
void setrad(float r) {rad = r;}
float area() {return PI*rad*rad;}
};
int main()
{
Point p;
Circle c;
float a = p.area();
cout<<a<<endl;
c.setrad(2.5);
a = c.area();
cout<<a<<endl;
}
分析:
float a=p.area()调用的是Point类的area函数:p是基类Point的对象;对象调用函数时採用静态联编;Point类的对象调用area,area一定是Point类的area;a=c.area()调用的是Circle类的area函数,由于就近原则。
改动main函数:
int main()
{
Point *p;
Circle c;
float a;
c.setrad(2.5);
p = &c;
a = p->area();//该函数属于静态联编
cout<<a<<endl;
}
程序输出:
0.0
分析:虽然基类指针指向了派生类对象,但仅仅可以通过基类的指针引用派生类对象从基类继承过去的成员。假设要想基类指针可以指向派生类的成员,那么就要使用虚函数,从而实现动态联编。
4.四种静态联编
(1)基类指针指向基类对象,採用静态联编调用基类成员。
(2)派生类的指针指向派生类的对象採用静态联编,调用派生类成员。若派生类没有的成员。调用基类的该成员。
(3)基类指针指向派生类对象,採用静态联编,调用基类成员。
(4)对象引用成员一定是静态联编。基类对象引用基类成员,派生类对象引用派生类成员,若派生类没有则能够引用基类成员。
注:派生类指针不可指向基类,引用也不能够。
5.动态联编
(1)条件:基类的同名函数被声明为虚函数。基类指针指向派生类对象。
(2)作用:使用动态联编。使得一个基类指针能够訪问多个派生类的成员函数,动态联编仅仅能通过指针或引用来实现(前提是虚函数机制)。
example 2
#include <iostream.h>
class Point
{
public:
virtual double area() {return 0.0;}
};
class Rectangle:public Point
{
double length, width;
public:
Rectangle(double l, double w):length(l), width(w) {}
double area() {return length*width;}
};
class Circle:public Point
{
double radium;
public:
Circle(double r) {radium = r;}
double area() {return 3.1416*radium*radium;}
};
class Triangle:public Point
{
double x, y, z;
public:
Triangle(double a, double b, double c)
{x = a; y = b; z = c;}
double area() {return (x+y+z)*0.5;}
};
int main()
{
Point *p;
Rectangle r(5.0, 2.0);
Circle c(6.0);
Triangle t(3.0, 4.0, 5.0);
p = &r;
cout<<p->area()<<endl;
p = &c;
cout<<p->area()<<endl;
p = &t;
cout<<p->area()<<endl;
}
程序输出:
10
113.098
6
注:基类的虚函数也能够被调用。调用方法:加类的作用域(强制採用静态联编)。
演示样例:
p = &r;
p->area();//调用Rectangle类的area函数
p->Point::area();//强制採用静态联编,调用Point类作用下的area函数
6.虚函数的訪问权限
(1)派生类中的虚函数不影响动态联编,基类的虚函数是保证动态联编的必要条件。
(2)一个类中的虚函数仅仅对派生类重定义的函数有影响,对它的基类成员无影响。
example 3
#include <iostream.h>
class A
{
public:
virtual void fun1()
{cout<<"fun1()...fun2()"<<endl; fun2();}
void fun2()
{cout<<"fun2()...fun3()"<<endl; fun3();}
virtual void fun3()
{cout<<"fun3()...fun4()"<<endl; fun4();}
virtual void fun4()
{cout<<"fun4()...fun5()"<<endl; fun5();}
void fun5() {cout<<"The end."<<endl;}
};
class B:public A
{
void fun3()
{cout<<"fun3...fun4"<<endl; fun4();}
void fun4()
{cout<<"fun4...fun5"<<endl; fun5();}
void fun5()
{cout<<"All done."<<endl;}
};
int main()
{
A *thing;
thing = new A;
thing->fun1();
thing = new B;
thing->fun1();
}
程序输出:
fun1()...fun2()
fun2()...fun3()
fun3()...fun4()
fun4()...fun5()
The end.
fun1()...fun2()
fun2()...fun3()
fun3...fun4
fun4...fun5
All done.
7.若基类和派生类中的同名函数參数个数不同。參数类型不同,基类中的成员函数虽然是虚函数。但将丢失虚特性——採用静态联编。
基类和派生类中的同名成员函数,若出现基类中的同名成员函数非虚,派生类中同名成员函数是虚函数。那么也将採用静态联编。
example 4
#include <iostream.h>
class Base
{
public:
virtual void fun1() {cout<<"Base fun1."<<endl;}
virtual void fun2() {cout<<"Base fun2."<<endl;}
void fun3() {cout<<"Base fun3."<<endl;}
void fun4() {cout<<"Base fun4."<<endl;}
};
class Device:public Base
{
public:
virtual void fun1()
{cout<<"Device fun1."<<endl;}
virtual void fun2()
{cout<<"Device fun2."<<endl;}
virtual void fun3()
{cout<<"Device fun3."<<endl;}
virtual void fun4()
{cout<<"Device fun4."<<endl;}
};
int main()
{
Base *pb;
Device d;
pb = &d;
pb->fun1();
pb->fun2();
pb->fun3();
pb->fun4();
}
程序输出:
Device fun1.
Device fun2.
Base fun3.
Base fun4.
C++语言笔记系列之十八——虚函数(1)的更多相关文章
- C++语言笔记系列之十二——C++的继承
C++的继承 1.继承方式 public(公有继承) 派生类中的成员能够訪问基类的public成员和protected成员,但不能訪问基类的private成员. 派生类的对象仅仅能訪问基类的publi ...
- C++语言笔记系列之十六——赋值兼容规则&多继承的二义性
1.赋值兼容规则 (1)派生类对象能够给基类对象赋值,这样的情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:可是不同意将一个基类的对象赋值给一个派生类. (2)能够将派生类对象的地址赋给基类 ...
- C++语言笔记系列之十——静态成员
1.静态成员 (1)由keywordstatic修饰 静态变量定义语句在编译阶段就运行,运行过程中不再运行. (2)分类:静态数据成员.静态成员函数. (3)静态成员时类的全部对象共享的成员,而不是某 ...
- Web 前端开发人员和设计师必读文章推荐【系列二十八】
<Web 前端开发精华文章推荐>2014年第7期(总第28期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...
- OSGi 系列(十八)之 基于注解的 Blueprint
OSGi 系列(十八)之 基于注解的 Blueprint 1. 注解实现 blueprint 第一步:bundle 添加 Bundle-Blueprint-Annotation <plugin& ...
- Java 设计模式系列(十八)备忘录模式(Memento)
Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...
- 【Java学习笔记之二十八】深入了解Java8新特性
前言: Java8 已经发布很久了,很多报道表明java8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java 8 ...
- ComicEnhancerPro 系列教程十八:JPG文件长度与质量
作者:马健邮箱:stronghorse_mj@hotmail.com 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程十八:JPG文件长度 ...
- Dynamic CRM 2013学习笔记(二十八)用JS动态设置字段的change事件、必填、禁用以及可见
我们知道通过界面设置字段的change事件,是否是必填,是否可见非常容易.但有时我们需要动态地根据某些条件来设置,这时有需要通过js来动态地控制了. 下面分别介绍如何用js来动态设置. 一.动态设 ...
随机推荐
- UI各种小控件的用法
今天给大家列举出来UI中的一些小控件的用法.方便大的学习与使用 一些方法和属性我们能够查看API文档.不必将每一个控件的功能都记住, 由于在使用的过程中,我们能够查看API文档.方便使用,我们仅仅要记 ...
- 2014.08.04,读书,读书笔记-《Matlab概率与数理统计分析》-第1章 MATLAB的数据基础
第1章 MATLAB数据基础 虽然一直间或使用MATLAB,但从来没有系统的学习过,现在开始也不晚.先对几个重点或者平时忽略的要点做下笔记. %后的所有文字为注释,多条命令可以放在一行,但要用逗号或分 ...
- Codeforces Gym 100015F Fighting for Triangles 状态压缩DP
F Fighting for Triangles Description Andy and Ralph are playing a two-player game on a triangular bo ...
- Linux下FFmpeg的安装编译过程【转】
本文转载自:http://www.linuxidc.com/Linux/2013-06/85628.htm 详细说下在Linux下FFmpeg的安装编译过程.参考 Ubuntu 10.04安装编译FF ...
- 最大似然估计的缺陷 —— 方差和均值的 bias
0. 均匀分布期望的最大似然估计 首先我们来看,如何通过最大似然估计的形式估计均匀分布的期望.均匀分布的概率密度函数为:f(x|θ)=1θ,0≤x≤θ.不失一般性地,将 x1,x2,-,xn 排序为顺 ...
- 对于NAS,IP SAN以及iSCSCI SAN存储的一些认识和理解
一直以来用户对于在选购存储产品上有许多不清楚,市场上有NAS, FC SAN,IP SAN和iSCSCI SAN产品,到底哪种类型的产品更适合支撑企业的应用系统呢? 我们经常可以听到用户讲: “NAS ...
- SpringBoot(十一) Dubbo分布式与Zookeeper
Dubbo简介 1.Dubbo简介 1. Dubbo是什么? dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbo这样的分布式服务框架的需求,并且本质上 ...
- DOM基础知识(Node对象、Element对象)
5.Node对象 u 遍历节点 u 父节点 .parentNode - 获取父节点—> 元素节点或文档节点 .parentElement - 获取父元素节点—> 元素节点 u 子节 ...
- jar文件配置冲突问题transformResourcesWithMergeJavaResForDebug
先看本人AS报错异常 Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. > c ...
- MySQL高级查询和编程基础
第一章 数据库设计 一.数据需求分析: 数据需求分析是为后续概念设计和逻辑结构设计做准备. 结构:(1)对现实世界要处理的对象进行详细的调查. (2)收集基础数.据. (3)对所收集的数据进行处理. ...