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++输入输出流的含义 以前所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出到显示器屏幕上.从操作系统的角度看,每一个与主机相连的输入输出设备都被看作一个文件.程序的输入指的 ...
随机推荐
- centos下 telnet访问百度
先在命令行输入以下命令: telnet www.baidu.com 80 点击确认之后出现如下界面 然后接着输入以下两行命令 GET /index.html HTTP/1.1 Host: www.ba ...
- 研究好vif 和vshow
另外从源头上处理的???,怎么自己排查出错误??必须 ??https://www.jb51.net/article/124116.htm
- Kafka 0.11.0.0 实现 producer的Exactly-once 语义(英文)
Exactly-once Semantics are Possible: Here’s How Kafka Does it I’m thrilled that we have hit an excit ...
- Use of Recv-Q and Send-Q
From my man page: Recv-Q Established: The count of bytes not copied by the user program connected to ...
- spring上下文和springMVC上下文的关系
查看原文
- Django-CRM项目学习(三)-stark的页面展示
开始今日份整理,主要为stark组件的增删改以及model_form组件的使用以及form组件的回顾 1.list_display_link功能 功能:客制化字段进行跳转到指定的页面(编辑页面) 1. ...
- https进行配置以及http跳转到https配置
https配置: nginx配置 server { listen 443; server_name localhost; ssl on; root html; index index.html ind ...
- mysql 报错ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executin
解决办法1. 修改用户密码mysql> alter user 'root'@'localhost' identified by 'youpassword'; 或者 mysql> set p ...
- webservice调用和生成
webservice简介: Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范实施的应用之间 ...
- 在Bootstrap开发框架中使用bootstrapTable表格插件和jstree树形列表插件时候,对树列表条件和查询条件的处理
在我Boostrap框架中,很多地方需要使用bootstrapTable表格插件和jstree树形列表插件来共同构建一个比较常见的查询界面,bootstrapTable表格插件主要用来实现数据的分页和 ...