【c++】面向对象程序设计之关于继承
面向对象程序设计的核心思想是数据抽象(类的接口与实现分离)、继承和动态绑定
基类
虚函数:基类希望派生类各自定义适合自身的版本的函数
在c++中,当我们使用基类的引用或指针调用虚函数时将发生动态绑定。
基类通常都应该定义一个虚析构函数。C++规定:用不带有虚析构函数的基类的指针来删除一个派生类对象(基类指针指向派生类对象,delete该指针),这个对象的派生类部分没有被析构,造成内存泄漏。
注意:
1.应该为多态基类声明虚拟析构函数。如果一个类有一个虚函数,那么它也应该有一个虚析构函数
2.如果一个类不是被设计为基类或者它们并不是按照多态的方式来使用的(不是用基类指针或引用来指向派生类对象),不要为它们声明虚析构函数 ,vptr及virtual table的存在使得内存空间浪费
任何构造函数之外的非静态函数都可以是虚函数,virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。
构造函数不能是虚函数的原因:
1.从存储空间角度
如果构造函数是虚函数,就需要通过 vptr来调用,vptr是存储在对象的内存空间中,可是对象还没有实例化,也就是内存空间还没有,怎么找vptr呢?所以构造函数不能是虚函数。
2.从使用角度来看
虚函数作用是通过父类的指针或引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数
static函数不能是虚函数的原因:
调用虚函数需要类的实例,static函数作为类函数,不属于类的实例,自然无法实现多态了。
派生类
派生类能访问public protected成员,不能访问private成员。
如果派生类没有覆盖其基类中的某个虚函数,则该虚函数的行为类似其他的普通成员,派生类会直接继承其在基类中的版本。c++11允许派生类显式地注明它使用某个成员函数覆盖了它继承的虚函数,在声明语句的最后添加override.
派生类必须使用基类的构造函数来初始化它的基类部分,通过构造函数初始化列表将实参传递给基类构造函数(否则使用默认构造函数)。首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。
派生类的声明包含类名但是不包含它的派生列表,派生列表必须与类的主体一起出现。
如果我们想将某个类用作基类,则该类必须已经定义而非仅仅声明。派生类中包含并且可以使用它从基类继承而来的成员,为了使用这些成员,派生类需要知道它们是什么,因此一个类不能派生它本身。vs中会报错:不允许使用不完整的类型
防止继承的发生:
c++11提供了一种防止继承发生的方法,即定义时在类名后跟关键字final。
当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略。
----------------------------------------------------------------------------------------------------------------
class ClxBase
{
public:
ClxBase() {};
virtual ~ClxBase() {}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
}; class ClxDerived : public ClxBase
{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
}; int main()
{
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest; system("pause");
}
输出结果是:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
如果把类ClxBase析构函数前的virtual去掉,那输出结果如下:
Do something in class ClxDerived!
上述例子中会先调用基类的构造函数,再调用派生类的构造函数;而析构时,先调用派生类的析构函数,再调用基类的析构函数
一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。
基类的析构函数一般都是虚函数是为了当删除一个指向派生类对象的基类指针时,派生类的析构函数会被调用。由函数的调用流程可以得知,先确定指针的静态类型,然后在静态类型对应的类中查找该函数,当找到后,如果该函数是虚函数,那么会在运行时根据指针的动态类型来调用函数,否则编译时直接生成调用当前类中的该函数的代码。所以若析构函数不是虚函数,当删除一个指向派生类对象的基类指针时不会调用派生类的析构函数。
【c++】面向对象程序设计之关于继承的更多相关文章
- java面向对象三大特性之继承
通过重用已经测试并验证通过的代码,怎样才减少开发工作,所有开发团队都在为一问题而努力.一个久经考验的方法是通过有效地使用Java继承优化应用程序开发. 继承的从某种意义上讲,继承的短暂美就如同宇宙中所 ...
- 周强 201771010141 《面向对象程序设计(java)》第七周学习总结
实验目的与要求 (1)进一步理解4个成员访问权限修饰符的用途: (2)掌握Object类的常用API用法: (3)掌握ArrayList类用法与常用API: (4)掌握枚举类使用方法: (5)结合本章 ...
- JavaScript 面向对象程序设计(下)——继承与多态 【转】
JavaScript 面向对象程序设计(下)--继承与多态 前面我们讨论了如何在 JavaScript 语言中实现对私有实例成员.公有实例成员.私有静态成员.公有静态成员和静态类的封装.这次我们来讨论 ...
- javascript之面向对象程序设计(对象和继承)
总结的文章略长,慎点. 知识点预热 引用类型:引用类型的值(对象)是引用类型的一个实例.在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起.在其他面向对象语言中被称为类,虽然 ...
- Java面向对象程序设计--与C++对比说明:系列3(Java 继承机制)
继承(inheritance)背后的核心思想是: bonus = b; } } Java没有像C++那样提供多继承机制,但提供了接口机制,在后面我们将详细探究接口机制的实现 ...
- Python基础(16)_面向对象程序设计(类、继承、派生、组合、接口)
一.面向过程程序设计与面向对象程序设计 面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题的简单化,流程化 缺点: ...
- [.net 面向对象程序设计深入](2)UML——在Visual Studio 2013/2015中设计UML用例图
[.net 面向对象程序设计深入](2)UML——在Visual Studio 2013/2015中设计UML用例图 1.用例图简介 定义:用例图主要用来描述“用户.需求.系统功能单元”之间的关系. ...
- [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图
[.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图 1.UML简介 Unified Modeling Language (UML)又称统 ...
- [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类
[.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类 本节导读: 关于JSON序列化,不能 ...
随机推荐
- CSS3-文本-text-shadow
一.text-shadow 语法: text-shadow : none | <length> none | [<shadow>, ] * <shadow> 或no ...
- 计算机/ARM 系统
这方面知识,自己有了大概了解,可以再没有重要急需学习的安排时,不时的翻翻这方面的书.本能力属于重要但不急需的能力
- Python9-模块1-day19
在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque.defaultdict.namedtuple和Ord ...
- Loadrunner中web脚本转成JavaVuser脚本
先用web方式录制好脚本,进行参数化,关联验证等操作,之后执行以下步骤: 1)将要转换的web脚本复制出来并保存到web.txt中2)参数界定符部分需要由{}手动修改成<>,包括参数化的和 ...
- windons杀死8080进程
1,netstat -aon|findstr "8080" //8080端口号 2,taskkill -PID 2976 -F //2976 ,8080端口号对应的进程号
- Knockout v3.4.0 中文版教程-15-控制文本内容和外观-attr绑定
6. attr绑定 目的 attr绑定可以给关联DOM元素的任何属性赋值.这个绑定很棒,比如,当你想要设置通过视图模型给元素的title属性.img标签的src属性或超链接的href值,当视图模型对应 ...
- 使用adb命令启查看已安装的Android应用的versionCode、versionName
列出已经安装的应用 adb shell pm list package C:\Users\CJTDEV003>adb shell pm list package package:com.sams ...
- Leetcode 331.验证二叉树的前序序列化
验证二叉树的前序序列化 序列化二叉树的一种方法是使用前序遍历.当我们遇到一个非空节点时,我们可以记录下这个节点的值.如果它是一个空节点,我们可以使用一个标记值记录,例如#. 例如,上面的二叉树可以被序 ...
- [uiautomator篇][11]wifi
package com.softwinner.network.wifi; import android.content.Context; import android.content.Intent; ...
- 九度oj 题目1125:大整数的因子
题目描述: 已知正整数k满足2<=k<=9,现给出长度最大为30位的十进制非负整数c,求所有能整除c的k. 输入: 若干个非负整数c,c的位数<=30每行一个c,当c=-1时中止 ( ...