《c++特性》

目录

  • 多态
    • 构造函数和析构函数存在多态吗?
    • 虚函数表
    • 虚析构函数
    • 纯虚函数和抽象类
    • 运行时多态和编译时多态的区别
  • 继承
    • 设计实例
    • 指针对象和普通对象的区别
    • 正确初始化派生类方式
    • 继承和赋值的兼容规则
    • protected 和 private 继承
    • 基类与派生类的指针强制转换
  • 如何用C实现C++的三大特性
    • C语言实现封装性
    • C语言实现继承
    • C语言实现多态

多态

在类的定义中,前面有 virtual 关键字的成员函数称为虚函数;
virtual 关键字只用在类定义里的函数声明中,写函数体时不用。
「派生类的指针」可以赋给「基类指针」;
通过基类指针调用基类和派生类中的同名「虚函数」时:
若该指针指向一个基类的对象,那么被调用是 基类的虚函数;
若该指针指向一个派生类的对象,那么被调用 的是派生类的虚函数。

调用哪个虚函数,取决于指针对象指向哪种类型的对象。
派生类的对象可以赋给基类「引用」
通过基类引用调用基类和派生类中的同名「虚函数」时:
若该引用引用的是一个基类的对象,那么被调 用是基类的虚函数;
若该引用引用的是一个派生类的对象,那么被 调用的是派生类的虚函数。
调用哪个虚函数,取决于引用的对象是哪种类型的对象。
在面向对象的程序设计中使用「多态」,能够增强程序的可扩充性,即程序需要修改或增加功能的时候,需要改动和增加的代码较少。
this 指针的作用就是指向成员函数所作用的对象, 所以非静态成员函数中可以直接使用 this 来代表指向该函数作用的对象的指针。
pBase 指针对象指向的是派生类对象,派生类里没有 fun1 成员函数,所以就会调用基类的 fun1 成员函数,在Base::fun1() 成员函数体里执行 this->fun2() 时,实际上指向的是派生类对象的 fun2 成员函数。

构造函数和析构函数存在多态吗?

在构造函数和析构函数中调用「虚函数」,不是多态。
一般不建议在构造函数或者析构函数中调用虚函数,因为在构造函数和析构函数中调用虚函数不会呈现多态性。

原因是啥呢?你想啊,在构造基类调用基类的构造函数时,派生类的部分还没有构造,怎么可能能用虚函数实现动态绑定派生生类对象呢,所以构造B基类部分的时候,调用的基类的函数bar;

对于foo函数不是虚函数不会有动态绑定,所以调用的基类部分;
对于第三个bar调用,是虚函数,实现动态绑定,所以调用的是派生类部分。

同样的道理,当调用继承层次中某一层次的类的析构函数时,往往意味着其派生类部分已经析构掉,所以也不会呈现出多态。

编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数。

虚函数表

每一个有「虚函数」的类(或有虚函数的类的派生类)都有一个「虚函数表」,该类的任何对象中都放着虚函数表的指针。「虚函数表」中列出了该类的「虚函数」地址。
可以发现有虚函数的类,多出了 8 个字节,在 64 位机子上指针类型大小正好是 8 个字节,多出来的 8 个字节就是用来放「虚函数表」的地址。
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
虚函数表的指针」指向的是「虚函数表」,「虚函数表」里存放的是类里的「虚函数」地址,那么在调用过程中,就能实现多态的特性。

虚析构函数

析构函数是在删除对象或退出程序的时候,自动调用的函数,其目的是做一些资源释放。

那么在多态的情景下,通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数,这就会存在派生类对象的析构函数没有调用到,存在资源泄露的情况。

解决办法:把基类的析构函数声明为virtual
派生类的析构函数可以 virtual 不进行声明;
通过基类的指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数,还是遵循「先构造,后虚构」的规则。

所以要养成好习惯:

一个类如果定义了虚函数,则应该将析构函数也定义成虚函数;
或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
注意:构造函数不能定义成虚构造函数
在构造基类调用基类的构造函数时,派生类的部分还没有构造,怎么可能能用虚函数实现动态绑定派生生类对象呢,所以构造B基类部分的时候,调用的基类的函数bar;

纯虚函数和抽象类

纯虚函数:没有函数体的虚函数。
包含纯虚函数的类叫抽象类

抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
抽象类的指针和引用可以指向由抽象类派生出来的类的对象

运行时多态和编译时多态的区别

编译时的多态,是指参数列表的不同, 来区分不同的函数, 在编译后, 就自动变成两个不同的函数。

运行时多态:用到的是后期绑定的技术, 在程序运行前不知道,会调用那个方法, 而到运行时, 通过运算程序,动态的算出被调用的地址. 运行时多态,也就是动态绑定,是指在执行期间(而非编译期间)判断所引用对象的实际类型,根据实际类型判断并调用相应的属性和方法

继承

派生类是通过对基类进行修改和扩充得到的,在派生类中,可以扩充新的成员变量和成员函数。
派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public。需要注意的是:在派生类的各个成员函数中,不能访问基类的 private 成员。
在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前,相当于基类对象是头部。派生类对象的大小 = 基类对象成员变量的大小 + 派生类对象自己的成员变量的大小

设计实例

假设要写一个小区养狗管理系统:

需要写一个「主人」类。
需要些一个「狗」类。

假定狗只有一个主人,但是一个主人可以最多有 10 条狗,应该如何设计和使用「主人」类 和「狗」类呢?我们先看看下面几个例子。

为狗类设一个主人类的对象指针;
为主人类设一个狗类的对象指针数组。

class CDog;
class CMaster // 主人类
{CDog  * pDogs[10]; // 狗类的对象指针数组
};class CDog  // 狗类
{CMaster * pm;   // 主人类的对象指针
};

在这里插入图片描述
因为相当于狗和主人是独立的,然后通过指针的作用,使得狗是可以指向一个主人,主人也可以同时指向属于自己的 10 个狗,这样会更灵活。

指针对象和普通对象的区别

如果不用指针对象,生成 A 对象的同时也会构造 B 对象。用指针就不会这样,效率和内存都是有好处的。

class Car
{Engine engine; // 成员对象Wing * wing;   // 成员指针对象
};

定义一辆汽车,所有的汽车都有 engine,但不一定都有 wing 这样对于没有 wing 的汽车,wing 只占一个指针,判断起来也很方便。
空间上讲,用指针可以节省空间,免于构造 B 对象,而是只在对象中开辟了一个指针,而不是开辟了一个对象 B 的大小。
效率上讲,使用指针适合复用。对象 B 不但 A 对象能访问,其他需要用它的对象也可以使用。
指针对象可以使用多态的特性,基类的指针可以指向派生链的任意一个派生类。
指针对象,需要用它的时候,才需要去实例化它,但是在不使用的时候,需要手动回收指针对象的资源。

正确初始化派生类方式

class Bug {
private :int nLegs; int nColor;
public:int nType;Bug (int legs, int color);void PrintBug (){ };
};Bug::Bug( int legs, int color)
{nLegs = legs;nColor = color;
}
class FlyBug : public Bug  // FlyBug 是Bug 的派生类
{int nWings;
public:FlyBug( int legs,int color, int wings);
};

正确的FlyBug 构造函数:
通过调用基类构造函数来初始化基类,在执行一个派生类的构造函数 之前,总是先执行基类的构造函数。所以派生类析构时,会先执行派生类析构函数,再执行基类析构函数。

// 正确的FlyBug 构造函数:
FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color)
{nWings = wings;
}

继承和赋值的兼容规则

派生类的对象可以赋值给基类对象
派生类对象可以初始化基类引用
派生类对象的地址可以赋值给基类指针
如果派生方式是 private 或 protected,则上述三条不可行。

protected 和 private 继承

protected 继承时,基类的 public 成员和 protected 成员成为派生类的 protected 成员;
private 继承时,基类的 public 成员成为派生类的 private 成员,基类的 protected 成员成 为派生类的不可访问成员;
派生方式是 private 或 protected,则是无法像 public 派生承方式一样把派生类对象赋值、引用、指针给基类对象。

基类与派生类的指针强制转换

public 派生方式的情况下,派生类对象的指针可以直接赋值给基类指针:

Base *ptrBase = & objDerived;

ptrBase 指向的是一个 Derived 派生类(子类)的对象。
*ptrBase 可以看作一个 Base 基类的对象,访问它的 public 成员直接通过 ptrBase 即可,但不能通过 ptrBase 访问 。objDerived 对象中属于 Derived 派生类而不属于基类的成员。
通过强制指针类型转换,可以把 ptrBase 转换成 Derived 类的指针。

Base * ptrBase = &objDerived;
Derived *ptrDerived = ( Derived * ) ptrBase;

如何用C实现C++的三大特性

C语言实现封装性

构体AchievePackage中有成员变量_a和两个函数指针,在InitStruct函数中被赋予两个函数的地址(函数名即为其地址,也可为&fun1,得到的值一样),故在此处InitStruct函数相当于该结构体的构造函数,既可以初始化其成员变量_a的值,也在对象定义的同时为其函数指针赋值(需显示调用)
在这里插入图片描述

C语言实现继承

两个类如果存在继承关系,其子类必定具有父类的相关属性(即变量)和方法(即函数)。
用“组合”去实现一下C语言中的继承:
结构体嵌套:
在这里插入图片描述

C语言实现多态

多态是通过父类的指针或引用,调用了一个在父类中是virtual类型的函数,实现动态绑定机制。
若想使用父类的指针/引用调用子类的函数,需要在父类中将其声明为虚函数(virtual),且必须与子类中的函数参数列表相同,返回值也相同。
C++的多态是通过覆盖实现的,即父类的函数被子类覆盖了!
父类的该函数为虚函数,告诉父类的指针/引用,你调用这个函数的时候必须看一看你绑定的对象到底是哪个类的对象,然后去那个类里调用该函数!

//C语言模拟C++的继承与多态typedef void (*FUN)();      //定义一个函数指针来实现对成员函数的继承struct _A       //父类
{FUN _fun;   //由于C语言中结构体不能包含函数,故只能用函数指针在外面实现int _a;
};struct _B         //子类
{_A _a_;     //在子类中定义一个基类的对象即可实现对父类的继承int _b;
};void _fA()       //父类的同名函数
{printf("_A:_fun()\n");
}
void _fB()       //子类的同名函数
{printf("_B:_fun()\n");
}

下面是测试代码:

//C语言模拟继承与多态的测试
_A _a;    //定义一个父类对象_a
_B _b;    //定义一个子类对象_b
_a._fun = _fA;        //父类的对象调用父类的同名函数
_b._a_._fun = _fB;    //子类的对象调用子类的同名函数_A* p2 = &_a;   //定义一个父类指针指向父类的对象
p2->_fun();     //调用父类的同名函数
p2 = (_A*)&_b;  //让父类指针指向子类的对象,由于类型不匹配所以要进行强转
p2->_fun();     //调用子类的同名函数

效果:在这里插入图片描述

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

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

相关文章

Scala中的while循环

在Scala中的while循环 (while loop in Scala) while loop in Scala is used to run a block of code multiple numbers of time. The number of executions is defined by an entry condition. If this condition is TRUE the code will run otherwise it will not run. Scala中…

牛客网与leetcode刷题(高频题中简单or中等的)

目录1、反转链表2、排序3、先序中序后序遍历4、最小的k个数5、子数组的最大累加和6、 用两个栈实现队列7、142. 环形链表 II8、20. 有效的括号9、最长公共子串(动态规划),磕磕绊绊10、二叉树之字形层序遍历11、重建二叉树12、LRU缓存13、合并两个有序链表15、大数加法16、一个二…

AMUL的完整形式是什么?

AMUL:阿南德牛奶联盟有限公司 (AMUL: Anand Milk Union Limited) AMUL is an abbreviation of Anand Milk Union Limited. It is an Indian milk product cooperative dairy organization that is based in the small town of Anand in the state of Gujarat. AMUL …

mochiweb 源码阅读(十一)

大家好,今天周六,继续接着上一篇,跟大家分享mochiweb源码。上一篇,最后我们看到了mochiweb_socket_server:listen/3函数: listen(Port, Opts, State#mochiweb_socket_server{sslSsl, ssl_optsSslOpts}) ->case moch…

Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理 想。有…

红黑树的实现

目录1、红黑树原理1、红黑树性质2、变换规则(从插入结点的角度来讲)1.变色2.左旋3.右旋3、删除结点需要注意的地方2、代码1、定义结点以及构造函数2、定义红黑树类以及声明它的方法3、左旋4、右旋5、插入操作6、修正操作7、删除操作3、参考链接1、红黑树…

118 - ZOJ Monthly, July 2012

http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId339 都是赛后做的。。。弱爆了 A题是找由2和5组成的数字的个数 直接打个表就行了 只是比赛的时候不知道怎么打表啊。。 View Code #include<cstdio> #include<cstring> #include<algorith…

edp1.2和edp1.4_EDP​​的完整形式是什么?

edp1.2和edp1.4EDP​​&#xff1a;电子数据处理 (EDP: Electronic Data Processing) EDP is an abbreviation of Electronic Data Processing. It alludes to the functioning of operations of commercial data, documents processing of storing, with the use of a compute…

css链接样式_CSS中的样式链接

css链接样式CSS样式链接 (CSS Styling Links) The links in CSS can be styled in various ways to make our website more presentable and attractive. The links can also be styled depending on their states e.g. visited, active, hover, etc. CSS中的链接可以通过各种方…

css中的媒体查询_CSS中的媒体查询

css中的媒体查询CSS | 媒体查询 (CSS | Media Queries) Creating a web page is not an easy task as it requires loads of content and data so that it becomes strongly responsive to the users. To do that various contents are even added e.g.: resources, informativ…

SharePoint2013安装组件时AppFabric时出现1603错误,解决方法:

采用PowerShell命令批量下载必备组件: 下载完成后&#xff0c;采用批处理命令安装必备组件。 注&#xff1a;SPS2013安装必备组件及批处理下载地址&#xff1a; 需要将必备组件放在安装文件的PrerequisiteInstallerFiles文件夹中&#xff0c;将PreReq2013.bat放在安装文件根目录…

《MySQL——数据表设计三大范式》

目录数据表设计范式第一范式第二范式第三范式数据表设计范式 第一范式 数据表中的所有字段都是不可分割的原子值。 字段值还可以继续拆分的&#xff0c;就不满足第一范式&#xff0c;如下&#xff1a; 下面这个&#xff0c;更加贴合第一范式&#xff1a; 范式设计得越详细&…

三道简单树型dp+01背包~~hdu1561,poj1947,zoj3626

以前学树型dp就是随便的看了几道题&#xff0c;没有特别注意树型dp中的小分类的总结&#xff0c;直到上次浙大月赛一道很简单的树型dp都不会&#xff0c;才意识到自己太水了&#xff5e;&#xff5e;come on&#xff01; hdu1561&#xff1a;题目给出了很多棵有根树&#xff0c…

css 字体图标更改颜色_在CSS中更改字体

css 字体图标更改颜色CSS字体属性 (CSS font properties ) Font properties in CSS is used to define the font family, boldness, size, and the style of a text. CSS中的字体属性用于定义字体系列 &#xff0c; 粗体 &#xff0c; 大小和文本样式 。 Syntax: 句法&#xf…

C++基础知识点整理

基本语法 1、static关键字的作用 1、全局静态变量 加了static关键字的全局变量只能在本文件中使用。 存储在静态存储区&#xff0c;整个程序运行期间都存在。 2、局部静态变量 作用域仍为局部作用域。 不过离开作用域之后&#xff0c;并没有销毁&#xff0c;而是贮存程序中&a…

组合问题 已知组合数_组合和问题

组合问题 已知组合数Description: 描述&#xff1a; This is a standard interview problem to make some combination of the numbers whose sum equals to a given number using backtracking. 这是一个标准的面试问题&#xff0c;它使用回溯功能将总和等于给定数字的数字进…

可变参数模板、右值引用带来的移动语义完美转发、lambda表达式的理解

可变参数模板 可变参数模板对参数进行了高度泛化&#xff0c;可以表示任意数目、任意类型的参数&#xff1a; 语法为&#xff1a;在class或者typename后面带上省略号。 Template<class ... T> void func(T ... args) {// }T:模板参数包&#xff0c;args叫做函数参数包 …

python 子图大小_Python | 图的大小

python 子图大小In some cases, the automatic figure size generated by the matplotlib.pyplot is not visually good or there could be some non-acceptable ratio in the figure. So, rather than allowing a pyplot to decide the figure size, we can manually define t…

《设计模式整理》

目录常见设计模式如何保证单例模式只有一个实例单例模式中的懒汉与饿汉模式OOP设计模式的五项原则单例模式中的懒汉加载&#xff0c;如果并发访问该怎么做常见设计模式 单例模式&#xff1a; 单例模式主要解决了一个全局使用的类频繁的创建和销毁的问题。 单例模式下确保某一个…

JSON学习资料整理

1.什么是JSON JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript的一个子集。 JSON采用完全独立于语言的文本格式&#xff0c;但是也使用了类似于C语言家族的习惯&#xff08;包括C, C, C#, Java, JavaScript, Perl, Python等&#xff09;。这些…