C++回顾day03---<多态>
一:错误理解下的多态
#include <iostream> using namespace std; class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
} void overrideFunc()
{
cout << "Parent override func" << endl;
}
}; class Child:public Parent //1.有继承
{
public:
Child()
{
cout << "Child construct" << endl;
} void overrideFunc() //2.有函数重写
{
cout << "Child override func" << endl;
}
}; void main()
{
Parent* p = new Child(); //3.父类指针指向子类对象
p->overrideFunc(); //4.进行多态调用
system("pause");
}
这里是根据指针类型来判断调用那个函数执行(这是不对的)--->虽然也叫作多态
二:错误讨论 :使用函数来讨论到底出错在哪里?
(一)再次回顾对象的产生(重点)(对于一个含有无参构造方法的类的对象的生成)
class Test
{
public:
Test()
{
cout << "Test()" << endl;
} Test(int a)
{
cout << "Test(int a)" << endl;
} void getInfo()
{
cout << "use getinfo" << endl;
}
}; void main()
{
Test t; //会调用无参构造函数产生一个对象,若是不存在无参构造函数,则这里编译报错
Test t2(5); //会调用有参构造函数进行产生对象
t.getInfo();
t2.getInfo(); system("pause");
}
重点:Test t3();是错误的!!!--->虽然声明不会报错,使用的时候会出错
原因://warning C4930: “Test t3(void)”: 未调用原型函数(是否是有意用变量定义的?)
会认为该语句表示声明一个名为a的函数,返回类型是Test。
(二)详细错误案例指出《重点了解》
class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
} void overrideFunc()
{
cout << "Parent override func" << endl;
}
}; class Child01:public Parent
{
public:
Child01()
{
cout << "Child01 construct" << endl;
} void overrideFunc()
{
cout << "Child01 override func" << endl;
}
}; class Child02 :public Parent
{
public:
Child02()
{
cout << "Child02 construct" << endl;
} void overrideFunc()
{
cout << "Child02 override func" << endl;
}
}; void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用
{
p->overrideFunc(); //想实现对象方法的动态调用
} void main()
{
Child01 c1;
Child02 c2;
testMulStatus(&c1);
testMulStatus(&c2); system("pause");
}
本意是通过同一个父类指针,实现对不同的子类的方法的调用
三:真正的多态实现《重点》
(一)多态原则
1.要有继承
2.要有virtual函数重写
3.要有父类指针指向子类对象
(二)代码测试
class Parent
{
public:
Parent()
{
cout << "Parent construct" << endl;
} virtual void overrideFunc()
{
cout << "Parent override func" << endl;
}
}; class Child01:public Parent
{
public:
Child01()
{
cout << "Child01 construct" << endl;
} virtual void overrideFunc()
{
cout << "Child01 override func" << endl;
}
}; class Child02 :public Parent
{
public:
Child02()
{
cout << "Child02 construct" << endl;
} virtual void overrideFunc()
{
cout << "Child02 override func" << endl;
}
}; void testMulStatus(Parent* p) //想通过一个父类指针实现对子类对象方法的动态调用
{
p->overrideFunc(); //想实现对象方法的动态调用
} void main()
{
Child01 c1;
Child02 c2;
testMulStatus(&c1);
testMulStatus(&c2); system("pause");
}
四:原理探析(虚函数表指针VPtr)
(一)多态中的虚函数表
当类中声明虚函数时,编译器就会在类中生成一个虚函数表
虚函数表是一个存储类成员虚函数指针的数据结构
虚函数表由编译器自动生成和维护
virtual成员函数会被编译器放入虚函数表中
存在虚函数时,每个类中都会有一个指向虚函数表的指针(vptr指针)
注意:虽然定义为虚函数,但是占用空间和原来成员函数大小是一样的,所以在内存上面唯一的区别在于多了一个虚函数指针(大小4字节)
(二)证明虚函数指针的存在
class Test
{
public:
int a;
int b;
public:
Test()
{ } void getInfo()
{
cout << "getInfo" << endl;
} void setInfo()
{
cout << "setInfo" << endl;
}
}; class Test02
{
public:
int a;
int b;
public:
Test02()
{ } virtual void getInfo()
{
cout << "getInfo" << endl;
} virtual void setInfo()
{
cout << "setInfo" << endl;
}
}; void main()
{
cout << sizeof(Test) << endl;
cout << sizeof(Test02) << endl; system("pause");
}
无论是一个虚函数,还是两个或者多个虚函数,最终内存大小相差4字节。所以一个成员函数--->虚成员函数是不增加空间大小的。多的那4字节是由于虚函数表指针的增加导致的
(三)多态上虚函数调用步骤
每个对象会含有一个虚函数指针vptr,指向虚函数表,当父类指针调用重写虚函数时:首先会去找vptr,根据vptr去找对应的子类虚函数表,从而找到对应对象的方法进行调用
(四)虚函数和普通函数调用的区别
.若是普通成员函数,编译器可以直接确定被调用成员函数(静态编链)
.若是虚函数:编译器要根据对象的vptr指针,找到虚函数表,找到对应的虚函数进行调用(动态编链)
.所以虚函数的调用效率要低于普通成员函数,所以出于效率,没有必要全部定义为虚函数
C++回顾day03---<多态>的更多相关文章
- Java I/O(二)其他常用的输入输出流PrintStream等、标准流重定向
四.FilterOutputStream.PrintStream PrintStream是继承自FilterStream类的,例如标准输出流System.out就是著名的PrintStream类对象. ...
- java.IO输入输出流:过滤流:buffer流和data流
java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...
- Java中IO流,输入输出流概述与总结
总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都是抽象类InputStream(字节输入 ...
- 第27章 java I/O输入输出流
java I/O输入输出流 1.编码问题 import java.io.UnsupportedEncodingException; /** * java涉及的编码 */ public class En ...
- java 对象输入输出流
对象的输入输出流的作用: 用于写入对象 的信息读取对象的信息. 对象的持久化. 比如:用户信息. ObjectInputStream : 对象输入流 ...
- 【转】输入/输出流 - 全面掌握IO
File类: 程序中操作文件和目录都可以使用File类来完成即不管是文件还是目录都是使用File类来操作的,File能新建,删除,重命名文件和目录,但File不能访问文件内容本身,如果需要访问文件本身 ...
- 输入输出流(IO)
输入输出流(IO)文件(File)java.io.File用于表示文件(目录),也就是说程序员可以通过File类在程序中操作硬盘上的文件和目录.File类只用于表示文件(目录)的信息(名称.大小等), ...
- Java输入/输出流体系
在用java的io流读写文件时,总是被它的各种流能得很混乱,有40多个类,理清啦,过一段时间又混乱啦,决定整理一下!以防再忘 Java输入/输出流体系 1.字节流和字符流 字节流:按字节读取.字符流: ...
- JAVA Io 缓冲输入输出流
java中提供带缓冲的输入输出流.在打开文件进行写入或读取操作时,都会加上缓冲,提高了IO读写性能. 1. BufferedInputStream 缓冲输入流 2. BufferedOutputStr ...
- C++输入输出流
一.C++输入输出流的含义 以前所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出到显示器屏幕上.从操作系统的角度看,每一个与主机相连的输入输出设备都被看作一个文件.程序的输入指的 ...
随机推荐
- Swift代理的使用
Swift代理的使用 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被称为遵循(conform)这个协议. 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议 ...
- Java - String 的字面量、常量池、构造函数和intern()函数
一.内存中的 String 对象 Java 的堆和栈 对于基本数据类型变量和对象的引用,也就是局部变量表属于栈内存: 而通过 new 关键字和 constructor 创建的对象存放在堆内存: 直接的 ...
- RHEL/Centos7 安装图形化桌面(转)
RHEL/Centos7 安装图形化桌面 Linux是一个多任务的多用户的操作系统,好多linux爱好者在安装完linux后经常遇到一个问题——没有图形化桌面.今天小编在安装RHEL7的时候,一步 ...
- 指定IP地址进行远程访问服务器设置方法(windows系统)
我们有很多服务器经常受到外界网络的干扰,入侵者们通过扫描3389端口爆破密码非法进入我们的服务器,这时,我们可以配置服务器IP 安全策略来限制一些IP访问,大大提高了服务器的安全. 实验环境: ...
- GitHub的初级使用
最近准备学习一个GitHub的使用 一.账号创建 1.百度找到GitHub官方网站(https://github.com/ ) 2.点击Sign up注册GitHub账号 下图为注册页面 第一步:填写 ...
- bsp makefile2
1. grep "bsp_dir" -r ./ -s --exclude-dir "*.git" 用这个加快目录定位-- 2.编译所有子目录 for dir ...
- 011_Python中单线程、多线程和多进程的效率对比实验
Python是运行在解释器中的语言,查找资料知道,python中有一个全局锁(GIL),在使用多进程(Thread)的情况下,不能发挥多核的优势.而使用多进程(Multiprocess),则可以发挥多 ...
- 《通过C#学Proto.Actor模型》之PID
PID对象是代表Actor对象的进程,是能过Actor.Spawn(props)获取的:它有什么成员呢?既然代理Actor,首先有一个ID,标识自己是谁,Actor在Spawn时可以命名这个ID,否则 ...
- rocketmq 4.4部署安装
官网下载:rocketmq-all-4.4.0-bin-release.zip 准备环境:centos7.6 Maven Java8+ 操作: 在工作目录中进行如下操作: /home/software ...
- codeforces#1011C. Fly (二分,注意精度)
题意:火箭经过1到n号星球,并回到1号星球,现在给出每消耗一砘燃油能带起的火箭质量a[i]和b[i],a[i]代表在第i个星球起飞,b[i]代表在第i个星球降落.求出最少消耗的汽油.保证:如果不能完成 ...