一:错误理解下的多态

#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---<多态>的更多相关文章

  1. Java I/O(二)其他常用的输入输出流PrintStream等、标准流重定向

    四.FilterOutputStream.PrintStream PrintStream是继承自FilterStream类的,例如标准输出流System.out就是著名的PrintStream类对象. ...

  2. java.IO输入输出流:过滤流:buffer流和data流

    java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...

  3. Java中IO流,输入输出流概述与总结

    总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都是抽象类InputStream(字节输入 ...

  4. 第27章 java I/O输入输出流

    java I/O输入输出流 1.编码问题 import java.io.UnsupportedEncodingException; /** * java涉及的编码 */ public class En ...

  5. java 对象输入输出流

    对象的输入输出流的作用: 用于写入对象 的信息读取对象的信息. 对象的持久化. 比如:用户信息.              ObjectInputStream   : 对象输入流            ...

  6. 【转】输入/输出流 - 全面掌握IO

    File类: 程序中操作文件和目录都可以使用File类来完成即不管是文件还是目录都是使用File类来操作的,File能新建,删除,重命名文件和目录,但File不能访问文件内容本身,如果需要访问文件本身 ...

  7. 输入输出流(IO)

    输入输出流(IO)文件(File)java.io.File用于表示文件(目录),也就是说程序员可以通过File类在程序中操作硬盘上的文件和目录.File类只用于表示文件(目录)的信息(名称.大小等), ...

  8. Java输入/输出流体系

    在用java的io流读写文件时,总是被它的各种流能得很混乱,有40多个类,理清啦,过一段时间又混乱啦,决定整理一下!以防再忘 Java输入/输出流体系 1.字节流和字符流 字节流:按字节读取.字符流: ...

  9. JAVA Io 缓冲输入输出流

    java中提供带缓冲的输入输出流.在打开文件进行写入或读取操作时,都会加上缓冲,提高了IO读写性能. 1. BufferedInputStream 缓冲输入流 2. BufferedOutputStr ...

  10. C++输入输出流

    一.C++输入输出流的含义 以前所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出到显示器屏幕上.从操作系统的角度看,每一个与主机相连的输入输出设备都被看作一个文件.程序的输入指的 ...

随机推荐

  1. 局部敏感哈希(LSH)之simhash和minhash

    minhash 1. 把文档A分词形成分词向量L 2. 使用K个hash函数,然后每个hash将L里面的分词分别进行hash,然后得到K个被hash过的集合 3. 分别得到K个集合中的最小hash,然 ...

  2. Linux VMware新添加网络适配器找不到配置文件问题

    VMware centos 新添加网卡没有识别,在做 Linux 实验时经常遇到需要双网卡的情况,VMware 新添加网卡后Linux是不会主动创建 ifcfg-eth* 文件的,重启服务器和重启网络 ...

  3. centos下 telnet访问百度

    先在命令行输入以下命令: telnet www.baidu.com 80 点击确认之后出现如下界面 然后接着输入以下两行命令 GET /index.html HTTP/1.1 Host: www.ba ...

  4. springboot + mybatis +pageHelper分页排序

    今天下午写查出来的数据的排序,原来的数据没有排序,现在把排序功能加上...原来用的,是xml中的sql动态传参 ,,1个小数没有弄出来,果断放弃... 网上百度一下,发现用pageHelper  可以 ...

  5. mysql partition分区

    (转) 自5.1开始对分区(Partition)有支持 = 水平分区(根据列属性按行分)=举个简单例子:一个包含十年发票记录的表可以被分区为十个不同的分区,每个分区包含的是其中一年的记录. === 水 ...

  6. openstack-mitaka部署

    详见我在51cto的一篇博客:http://egon09.blog.51cto.com/9161406/1839667

  7. Vue-项目打包上线

    一.打包生成dist目录 运行npm run build 进行打包,控制台显示“Build complete”表示打包完成了. npm run build 二.dist目录放到后端跟目录 打包后生成一 ...

  8. [Oracle]Sqlplus 中使用 new_value

    通过再sqlplus 中使用 new_value,可以把从表中查询出来的值,放置到 变量中.然后使用变量时,类似与宏定义一样,就可以像使用表中字段一样方便. 这使得sqlplus 的脚本具备和pl/s ...

  9. Redis入门之增删改查等常用命令总结

    Redis是用C语言实现的,一般来说C语言实现的程序"距离"操作系统更近,执行速度相对会更快. Redis使用了单线程架构,预防了多线程可能产生的竞争问题. 作者对于Redis源代 ...

  10. MaxCompute教程

    MaxCompute全套攻略:https://blog.csdn.net/qq_35394891/article/details/82943894 [2019年大数据福利推荐]MaxCompute教程 ...