C++基础15-类和对象之多态

总结:

1、在父类中申明虚函数时,一般情况下在子类中也申明(便于读代码)

一、赋值兼容

赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。

赋值兼容是一种默认行为,不需要任何的显示的转化步骤。

赋值兼容规则中所指的替代包括以下的情况:

         派生类的对象可以赋值给基类对象。

         派生类的对象可以初始化基类的引用。

         派生类对象的地址可以赋给指向基类的指针。

在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。

二、多态

C++中所谓的多态(polymorphism)是指,由继承而产生的相关的不同的类,其对象同一消息会作出不同的响应

如果父类指针指向的是父类对象则调用父类中定义的函数

如果父类指针指向的是子类对象则调用子类中定义的重写函数

1解决方案:

       C++中通过virtual关键字对多态进行支持

       使用virtual声明的函数被重写后即可展现多态特性

将需要表现多态的父类中的函数声明为虚函数,即在原有函数前加virtual关键字,并在子类函数中对父类虚函数进行重写

2、多态成立的条件

       <1>、要有继承

        <2>、要有虚函数重写

        <3>、要有父类指针(引用)指向子类对象

3、多态的实现原理

      虚函数表和vptr指针

      (1)当类中声明虚函数时,编译器会在类中生成一个虚函数表,

                虚函数表是一个存储类成员函数指针的数据结构;

               虚函数表是由编译器自动生成与维护的;

               virtual成员函数会被编译器放入虚函数表中;

        (2)存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。

说明:

1. 通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

2.出于效率考虑,没有必要将所有成员函数都声明为虚函数.

3.C++编译器,执行run函数,不需要区分是子类对象还是父类对象,而是直接通过p的VPTR指针所指向的对象函数执行即可。

4、验证vptr指针的存在(从多个类继承虚函数就有多个vptr指针)

         两个类:类A,类B,都只有数据成员int a,类A中有虚函数,类B中没有。计算sizeof(A) sizeof(B)

32位:sizeof(A) =8   sizeof(B)=4

64位:sizeof(A) =16   sizeof(B)=4  (64位一个指针占8字节,类的内存遵从字节对齐。故对齐后为8+8=16)

5、vptr指针初始化

vptr指针时分步骤初始化
       1、调用父类构造函数在调用父类构造器时,会将vptr指针当做父类来处理。此时会临时指向父类的虚函数表
       2、处理自己构造函数,此时将子类对象的空间变成子类对象处理,vptr指针就从指向父类的表 变成 指向子类的表
注:不要在构造函数中处理业务。调用虚函数

6、父类指针步长和子类指针步长

父类指针步长为 sizeof(父类)

子类指针步长为 sizeof(子类)

注:当父类指针指向子类对象时,指针加加,为父类指针步长,写一个可能并不是子类对象(父类所占空间小于等于子类空间,父类空间++,可能指针指向的并不是子类的开始地址)

验证vptr指针存在代码示例:

#if 1
#include<iostream>
//http://www.mamicode.com/info-detail-1394321.html
using namespace std;
class Parent1 {
public:virtual void func() {cout << "Parent" << endl;}
private:int a;
};
class Parent2 {
public:virtual void func() {cout << "Parent" << endl;}virtual void func(int a) {cout << "Parent" << endl;}
private:int a;
};
class Parent3 {
public:void func() {cout << "Parent" << endl;}
private:int a;
};
class Parent4 {
public:void func() {cout << "Parent" << endl;}
private:int a;
};void test01() {Parent1 p1;Parent2 p2;Parent3 p3;Parent4 p4;cout << "sizeof(parent1)" << sizeof(p1) << endl;  //多出来的字节是vptr指针所占用空间cout << "sizeof(parent2)" << sizeof(p2) << endl;cout << "sizeof(parent3)" << sizeof(p3) << endl;cout << "sizeof(parent4)" << sizeof(p4) << endl;}
//字节对齐
/*
sizeof(parent1)16
sizeof(parent2)16
sizeof(parent3)4
sizeof(parent4)4
*/int main() {test01();return 0;
}
#endif

vptr指针初始化代码示例:

#if 1
#include<iostream>
using namespace std;
//vptr指针时分步骤初始化
//1、调用父类构造函数
//2、处理自己构造函数
//不要在构造函数中处理业务。调用虚函数
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;print();  //调用父类print还是子类print?父类printfunc();}virtual void print() {cout << "Parent::print()...a="<<this->a << endl;}void func() {cout << "Parent:func" << endl;}
private:int a;
};
class Child :public Parent {
public:Child(int a, int b) :Parent(a) //在调用父类构造器时,会将vptr指针当做父类来处理//此时会临时指向父类的虚函数表{//将子类对象的空间变成子类对象处理//vptr指针就从指向父类的表 变成 指向子类的表cout << "Child(int,int)..." << endl;this->b = b;print();func();Parent::func();}virtual void print() {cout << "Child::print()...b=" <<this->b<< endl;}void func() {cout << "Child:func" << endl;}
private:int b;
};
void test01() {Parent *pp = new Child(10, 20);pp->print();   //发生多态delete pp;
}
/*
Parent(int)...
Parent::print()...a=10
Parent:func
Child(int,int)...
Child::print()...b=20
Child:func
Parent:func
Child::print()...b=20
*/
int main() {test01();return 0;
}
#endif

父类指针步长和子类指针步长示例:

#if 1
#include<iostream>
using namespace std;
#if 0
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a;
};
class Child :public Parent {
public:Child(int a) :Parent(a){cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;}
};
void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();}
}
void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++;  //pp+sizeof(Parent)cp++;  //cp+sizeof(Child)pp->print();cp->print();
}
void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0],i = 0; i < 3; i++, pp++) {pp->print();}
}
void test04() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();}
}
#endif
//在上面的代码基础上,在Child加int 运行报错 当pp++时报错
//因为    pp++;  //pp+sizeof(Parent)
//        cp++;  //cp+sizeof(Child)
//两个步长不一样。
class Parent {
public:Parent(int a) {cout << "Parent(int)..." << endl;this->a = a;}virtual void print() {cout << "Parent::print()...a=" << this->a << endl;}public:int a;
};
class Child :public Parent {
public:Child(int a) :Parent(a) {cout << "Child(int,int)..." << endl;//print();}virtual void print() {cout << "Child::print()...b=" << this->a << endl;}
public:int b;
};
void test01() {Child array[] = { Child(0),Child(1),Child(2) };for (int i = 0; i < 3; i++){array[i].print();}
}
/*
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=1
Child::print()...b=2
*/
void test02() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];pp->print();Child *cp = &array[0];cp->print();pp++;  //pp+sizeof(Parent)cp++;  //cp+sizeof(Child)pp->print();cp->print();
}
/*
程序崩溃
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=0
*/
void test03() {Child array[] = { Child(0),Child(1),Child(2) };Parent *pp = &array[0];int i = 0;for (pp = &array[0], i = 0; i < 3; i++, pp++) {pp->print();}
}
/*
程序崩溃
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
*/
void test04() {Child array[] = { Child(0),Child(1),Child(2) };Child *cp = &array[0];int i = 0;for (cp = &array[0], i = 0; i < 3; i++, cp++) {cp->print();}
}
/*
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Parent(int)...
Child(int,int)...
Child::print()...b=0
Child::print()...b=1
Child::print()...b=2
*/
int main() {//test01();//test02();test03();//test04();return 0;
}
#endif

代码示例:

1、多态

#if 1
#include<iostream>
#include<string>
using namespace std;
#if 0
class Hero {
public:string kongfu;
public:Hero(string kongfu) {this->kongfu = kongfu;}void fight() {cout << "英雄使出了" << this->kongfu << endl;}
};
class Yuebuqun :public Hero {
public:Yuebuqun(string kongfu):Hero(kongfu){}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;}
};class Linghuchong :public Yuebuqun
{
public:Linghuchong(string kongfu) :Yuebuqun(kongfu){}void  fight(){cout << "Linghuchong使出了"  << this->kongfu << endl;}
};
void fight(Hero *hero) {cout << "调用打人的方法" << endl;hero->fight();//希望传递进来的如果是子类,调用子类的fight//如果传递进来的是父类, 调用父类的fight//这种行为就是 多态行为。
}
void test01() {Hero h1("高招");h1.fight();Yuebuqun y1("y高招");y1.fight();Linghuchong l1("l高招");l1.fight();
}
/*
英雄使出了高招
Yuebuqun使出了y高招
Linghuchong使出了l高招
*/
void test02() {Yuebuqun *y1 = new Yuebuqun("y高招");y1->fight();Linghuchong *l1 = new Linghuchong("l高招");l1->fight();delete y1;delete l1;
}
/*
Yuebuqun使出了y高招
Linghuchong使出了l高招
*/
void test03() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");fight(y1);fight(l1);delete y1;delete l1;
}
/*
调用打人的方法
英雄使出了y高招
调用打人的方法
英雄使出了l高招
*/int main() {test01();//test02();//test03();return 0;
}
#endif
class Hero {
public:string kongfu;
public:Hero(string kongfu) {this->kongfu = kongfu;}virtual void fight() {cout << "英雄使出了" << this->kongfu << endl;}
};
class Yuebuqun :public Hero {
public:Yuebuqun(string kongfu) :Hero(kongfu) {}void fight() {cout << "Yuebuqun使出了" << this->kongfu << endl;}
};class Linghuchong :public Yuebuqun
{
public:Linghuchong(string kongfu) :Yuebuqun(kongfu) {}void  fight() {cout << "Linghuchong使出了" << this->kongfu << endl;}
};
void fight(Hero *hero) {cout << "调用打人的方法" << endl;hero->fight();//当把父类方法声明为virtual时。可以实现传进来谁,调用谁的方法//希望传递进来的如果是子类,调用子类的fight//如果传递进来的是父类, 调用父类的fight//这种行为就是 多态行为。
}void test04() {Yuebuqun *y1 = new Yuebuqun("y高招");Linghuchong *l1 = new Linghuchong("l高招");Hero *h1 = new Hero("h高招");fight(y1);  fight(l1);fight(h1);delete y1;delete l1;delete h1;
}
/*
调用打人的方法
Yuebuqun使出了y高招
调用打人的方法
Linghuchong使出了l高招
调用打人的方法
英雄使出了h高招
*/
int main() {test04();
}#endif

2、多态意义

#if 1
#include<iostream>
using namespace std;
//英雄类
class Hero {
public:Hero() {cout << "dd" << endl;}virtual int getAd() {return 10;}
};
class AdvHero:public Hero {
public:virtual int getAd() {  //virtual写不写都ok,写上能加强代码的可读性return 1001;}
};
//怪兽类
class Monster {
public:int getAd() {return 1000;}
};//战斗方法
void playerFight(Hero *hp, Monster *mp) {if (hp->getAd() > mp->getAd()) {  //hp->getAd()发生了多态cout << "英雄胜利,怪兽被打死了" << endl;}elsecout << "英雄死亡,怪兽赢了" << endl;
}//2020年
//拓展新东西  不改变之前代码
//多态意义 架构意义
class BugHero :public Hero {
public:virtual int getAd() {cout << "调用BugHero方法" << endl;return 666666;}
};
void test01() {Hero h;Monster m;playerFight(&h, &m);AdvHero advH;playerFight(&advH, &m);BugHero b;playerFight(&b, &m);
}int main() {test01();Hero *h1[6];  //不调用析构函数 没有具体指向//int array[][2];cout << "dddddddsfad" << endl;Hero h[5];  //调用5次构造函数
}
/*
dd
英雄死亡,怪兽赢了
dd
英雄胜利,怪兽被打死了
dd
调用BugHero方法
英雄胜利,怪兽被打死了
dddddddsfad
dd
dd
dd
dd
dd
*/
#endif

多态原因:

#if 0
#include<iostream>
using namespace std;class Parent {
public:Parent(int a) {this->a = a;}virtual void func(int a) {cout << "Parent:func(int).." << endl;}void PrintP(){}
private:int a;
};
class Child :public Parent {
public:Child(int a, int b) :Parent(b) {this->b = b;}virtual void func(int b) {cout << "Child::func(int)" << endl;}void func(int a, int b) {cout << "Child:func(int a,int b)" << endl;}void printC(){}
private:int b;
};void myFunc(Parent *p) {p->func(10);
}
void test01() {Parent *pp = new Parent(10);Parent *cp = new Child(100,200);  //父类指针指向子类对象myFunc(pp);cout << "------" << endl;myFunc(cp);  cp->PrintP();  //如果调用普通函数,编译器根本不会查找虚函数表//只有你调用的是虚函数,才会去查找虚函数表
}
int main() {test01();return 0;
}
#endif

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/567860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

傲云浏览器linux,Centos7安装部署zabbix监控软件

目录部署监控服务器部署监控服务器Zabbix ServerWeb页面验证设置部署监控服务器一、安装LNMP环境Zabbix监控管理控制台需要通过Web页面展示出来&#xff0c;并且还需要使用MySQL来存储数据&#xff0c;因此需要先为Zabbix准备基础LNMP环境。1. wget下载官网Nginxwget http://ng…

c语言环境变量的作用,C语言获取系统环境变量

C语言获取系统环境变量可以通过如下代码实现.#include #include char *platform(){//获取系统变量信息char *ret;extern char **environ;char **env environ;//打印系统变量信息/*while(*env){//puts(*env);env;}*/ret getenv("OS"); //for windows_ntif(NULL ! re…

mysql索引创建及使用注意事项

总结&#xff1a; 1、在使用索引时&#xff0c;一般情况下不建议使用like操作。如果使用&#xff0c;则%放在后面。否则不会使用索引。like ‘%abd%’不会使用索引,而like ‘aaa%’可以使用索引.&#xff08;最左缀原则&#xff09; 2、单列索引的使用&#xff1a; 《1》 只…

mulitpartfile怎么接收不到值_和平精英信号接收区和信号值是什么?信号值怎么恢复...

[闽南网]和平精英公测开启&#xff0c;和平精英与刺激战场有什么不同呢&#xff1f;今天小编就为大家带来了信号值详解&#xff01;各位玩家千万不要错过呀&#xff01;信号值详解信号接收区和信号值是什么&#xff0c;对选手有什么影响&#xff1f;在游戏战斗界面中&#xff0…

制备pdms膜的方法_船体用钢板基底超疏水表面的制备和性能

鲨鱼皮具有神奇的微纳双层结构&#xff0c;其微米级肋条状结构在水中的整流效果可减小水的阻力。纳米级刺状突起或刚毛具有疏水特性&#xff0c;使植物抱子很难附着其上&#xff0c;海藻等植物也不能在其表面生长&#xff3b;1,2&#xff3d;。这种微纳结构及其疏水性的共同作用…

递归题型解析

#include<iostream> using namespace std; int foo(int n) {if (n < 1)return n;return (foo(n - 1) foo(n - 2)); } int main() {printf("%d\n", foo(5));return 0; } 解析&#xff1a; foo(5)foo(4)f00(3)foo(3)foo(2)foo(3)2foo(3)foo(2)2(foo(2)foo(1…

64位c语言调用32位glibc,glibc fclose源代码阅读及伪造_IO_FILE利用fclose实现任意地址执行...

简介最近学习了一下_IO_FILE的利用&#xff0c;刚好在pwnable.tw上碰到一道相关的题目。拿来做了一下&#xff0c;遇到了一些困难&#xff0c;不过顺利解决了&#xff0c;顺便读了一波相关源码&#xff0c;对_IO_FILE有了更深的理解。文章分为三部分&#xff0c;分别是利用原理…

戴尔笔记本电脑开机黑屏怎么办_戴尔笔记本电脑充不进电怎么办

笔记本电脑电池充不进电要怎么办呢&#xff1f;笔记本电脑之所以这么受欢迎&#xff0c;是因为笔记本有配备电池&#xff0c;能够在没有电源的情况下使用五六个小时。而电池的电用光后&#xff0c;就需要进行充电。不过有些用户反映说&#xff0c;自己的电池充不进电&#xff0…

IIS安装2个SSL_顶级域名0元撸-免费注册2个腾讯云域名 免费SSL证书

前言这两天折腾甜糖CDN&#xff0c;为了收益最大化申请了公网IP&#xff0c;于是顺带折腾了一下群晖外网访问。使用的DDNS方案是腾讯dnspod&#xff0c;注册一个便宜的顶级域名访问我的群晖&#xff0c;折腾过程中发现可以免费注册2个顶级域名&#xff0c;不敢独享发出来大家一…

三菱a系列motion软体_工控电缆如何制作?(以三菱PLC、触摸屏为例)

RS232接口的三菱Q系列PLC编程通讯电缆三菱GT11/GT15触摸屏RS232串口编程电缆三菱GT11/GT15触摸屏连接Q系列PLC电缆三菱GT11/GT15触摸屏连接FX2/FX2C/A/QnA系列PLC电缆三菱GT11/GT15 触摸屏连接FX3U/FX2N/FX1N系列PLC电缆FX2、A系列PLC到A970GOT人机介面连接电缆FX0s/FX0n/FX2n/…

电脑入门完全自学手册_「新书推荐」新能源汽车维修完全自学手册

《新能源汽车维修完全自学手册》作者&#xff1a;广州瑞佩尔信息科技有限公司 、胡欢贵售价&#xff1a;85.00上市时间&#xff1a;2020年7月本书内容分为 8 章, 第 1 章为高压安全系统, 主要介绍了新能源汽车中高压安全防护装置构造以及维修所需的安全防护工具、 安全作业规范…

C/C++混淆点-左移右移操作符

对一个数实行左移或者右移操作&#xff0c;即先把操作数转换为二进制&#xff0c;然后左移&#xff08;>>&#xff09;即从左到右开始舍弃&#xff0c;右移&#xff08;<<&#xff09;即从各位之后开始加0。最后再转换为十进制。 #include<iostream> using…

ar路由器 pppoe下发ipv6 dns_IPv6网络设置各种疑难杂症诊疗区

1、Windows电脑系统IPv6无网络访问权限怎么解决&#xff1f;Win7系统下连接IPv6无网络访问权限的解决方法&#xff08;1&#xff09;首先修复网络连接&#xff0c;Win XP操作系统的网络连接有“修复”选项&#xff0c;Win7没有&#xff0c;不过可以使用“诊断”选项&#xff0c…

c语言判断化学方程式,下列是某同学写的六个化学方程式:①Mg+O2点燃.MgO2②C+O2点燃.CO...

化学方程式是最重要的化学语言&#xff0c;正确、熟练地书写化学方程式是学习化学必需具备的重要基本功。怎样书写化学方程式?1.要遵循两个基本原则(1)以客观事实为基础化学方程式既然是化学反应的表达形式&#xff0c;显然&#xff0c;有某一反应存在&#xff0c;才能用化学方…

tensorboard ckpt pb 模型的输出节点_“技术需求”与“技术成果”项目之间关联度计算模型 TOP10 baseline...

竞赛网址&#xff1a;传送门线上分数&#xff1a;0.78490746000&#xff0c;目前可以进入前10参考了苏神的代码&#xff0c;非常感谢&#xff1a;传送门调参技巧&#xff1a;传送门中文bert权重&#xff1a;传送门#! -*- coding:utf-8 -*-

白盒测试六种方法案例分析

1、语句覆盖 2、判定覆盖 3、条件覆盖 4、判定/条件覆盖 5、组合覆盖 6、路径覆盖

android 启动优化方案,Android 项目优化(五):应用启动优化

介绍了前面的优化的方案后&#xff0c;这里我们在针对应用的启动优化做一下讲解和说明。一、App启动概述一个应用App的启动速度能够影响用户的首次体验&#xff0c;启动速度较慢(感官上)的应用可能导致用户再次开启App的意图下降&#xff0c;或者卸载放弃该应用程序。应用程序启…

排序总结

排序总结 快速排序基本思路&#xff1a; 基本思想&#xff1a; 1&#xff09;选择一个基准元素,通常选择第一个元素或者最后一个元素, 2&#xff09;通过一趟排序讲待排序的记录分割成独立的两部分&#xff0c;其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值…

iphone最新款手机_苹果用户不换安卓手机的8点原因,最后一点最关键

快速的更新换代似乎已经成为了安卓手机的发展规律。与之相比&#xff0c;苹果公司的发展却逐渐从之前的“特立独行”变为了“固步自封”&#xff0c;产品风格也由原先的桀骜不驯&#xff0c;转变为现在向市场的不断妥协。即便如此&#xff0c;仍旧有很大一部分苹果用户没有考虑…

windows和android双系统平板,Windows平板打造双系统爽玩安卓APP

随着微软的surface更新换代&#xff0c; Windows平板是越来越受欢迎&#xff0c;在工作上&#xff0c;win平板处理办公文件和兼容性的优点得到了大家认可。但是在视听娱乐等休闲方面&#xff0c;始终还是没有安卓平板来的好用&#xff0c;而且安卓平台有大量好玩的APP&#xff…