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++输入输出流的含义 以前所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出到显示器屏幕上.从操作系统的角度看,每一个与主机相连的输入输出设备都被看作一个文件.程序的输入指的 ...
随机推荐
- 在搭建tesseract-OCR环境中遇到问题和反省
Tesseract,一款由HP实验室开发由Google维护的开源OCR(Optical Character Recognition , 光学字符识别)引擎,特点是开源,免费,支持多语言,多平台. 在搭 ...
- Java多线程基础(二)
1.多线程数据安全 线程同步:多个线程需要访问同一资源时,需要以某种顺序来确定该资源某一时刻只能被一个线程使用.从而,解决并发操作可能带来的异常. 2.同步代码块实现同步(部分代码的访问,我们希望它同 ...
- python——python3.6环境搭建(Windows10,64位)
1.python软件资源下载 1.1 打开python官网地址:https://www.python.org 1.2 根据自己电脑的设置选择下载合适的python3.6.2 1.3 此处选择windo ...
- Vue学习之路1-集成环境安装
1.前言 Vue 是一款友好的.多用途且高性能的javascript框架,与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用,它能够帮你创建可维护性和可测试性更强的代码库,Vue是渐进式的j ...
- 18 python 初学(time、random 模块)
# _author: lily # _date: 2019/1/13 import time import datetime print(help(time)) # print(time.time() ...
- Raft与MongoDB复制集协议比较
在一文搞懂raft算法一文中,从raft论文出发,详细介绍了raft的工作流程以及对特殊情况的处理.但算法.协议这种偏抽象的东西,仅仅看论文还是比较难以掌握的,需要看看在工业界的具体实现.本文关注Mo ...
- python购物车demo
product_list = [ ('Iphone',11800), ('Mac Pro',13800), ('BMW CAR',480000), ...
- Linux下redis的安装及配置
1.去官网下载redis(redis.io) 2.将其解压到根目录下 3.进入解压的目录,然后编译源程序, 如果不是root账户登录的,命令前面需要加sudo make make install PR ...
- ConcurrentHashMap 与 Hashtable
粘贴复制于:https://blog.csdn.net/lzwglory/article/details/79978788 集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构 ...
- MySQL与MongoDB
MySQL MongoDB DB DB table Collections row Documents column Field 增 db.tables.insert({})#效 ...