面向对象程序设计的核心思想是数据抽象(类的接口与实现分离)、继承和动态绑定

基类

虚函数:基类希望派生类各自定义适合自身的版本的函数

在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++】面向对象程序设计之关于继承的更多相关文章

  1. java面向对象三大特性之继承

    通过重用已经测试并验证通过的代码,怎样才减少开发工作,所有开发团队都在为一问题而努力.一个久经考验的方法是通过有效地使用Java继承优化应用程序开发. 继承的从某种意义上讲,继承的短暂美就如同宇宙中所 ...

  2. 周强 201771010141 《面向对象程序设计(java)》第七周学习总结

    实验目的与要求 (1)进一步理解4个成员访问权限修饰符的用途: (2)掌握Object类的常用API用法: (3)掌握ArrayList类用法与常用API: (4)掌握枚举类使用方法: (5)结合本章 ...

  3. JavaScript 面向对象程序设计(下)——继承与多态 【转】

    JavaScript 面向对象程序设计(下)--继承与多态 前面我们讨论了如何在 JavaScript 语言中实现对私有实例成员.公有实例成员.私有静态成员.公有静态成员和静态类的封装.这次我们来讨论 ...

  4. javascript之面向对象程序设计(对象和继承)

    总结的文章略长,慎点. 知识点预热 引用类型:引用类型的值(对象)是引用类型的一个实例.在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起.在其他面向对象语言中被称为类,虽然 ...

  5. Java面向对象程序设计--与C++对比说明:系列3(Java 继承机制)

    继承(inheritance)背后的核心思想是:       bonus = b;    }      } Java没有像C++那样提供多继承机制,但提供了接口机制,在后面我们将详细探究接口机制的实现 ...

  6. Python基础(16)_面向对象程序设计(类、继承、派生、组合、接口)

    一.面向过程程序设计与面向对象程序设计 面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题的简单化,流程化 缺点: ...

  7. [.net 面向对象程序设计深入](2)UML——在Visual Studio 2013/2015中设计UML用例图

    [.net 面向对象程序设计深入](2)UML——在Visual Studio 2013/2015中设计UML用例图  1.用例图简介 定义:用例图主要用来描述“用户.需求.系统功能单元”之间的关系. ...

  8. [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图

    [.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图 1.UML简介 Unified Modeling Language (UML)又称统 ...

  9. [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类

    [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类 本节导读: 关于JSON序列化,不能 ...

随机推荐

  1. POJ-1061 青蛙的约会 (扩展欧几里得)

    [题目描述] 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有 ...

  2. JavaScript脚本在页面中放置的位置

    JavaScript脚本通常放置在三个位置: 1.head部分JavaScript脚本. 2.body部分JavaScript脚本. 3.单独以.js结尾的文件中的JavaScript脚本. 客户端会 ...

  3. LeetCode01--回文数

    ''' 判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解释: ...

  4. magic mouse 2 在Mac上灵敏度太低的解决办法

    1.打开终端 2.输入以下代码查看当前鼠标移动速度 defaults read -g com.apple.mouse.scaling 你会看到输出的是 “3”,这是初始速度 3.输入以下代码改变鼠标移 ...

  5. python模拟浏览器webdriver登陆网站后抓取页面并输出

    关键在于以下两行代码 特别是find_element_by_xpath写法 很多写成 findElementsByXpath不知道是写错了 还是高级版本是这么写的... #webElement = s ...

  6. unittest的discover方法使用

    使用unittest进行测试,如果是需要实现上百个测试用例,把它们全部写在一个test.py文件中,文件会越来越臃肿,后期维护页麻烦.此时可以将这些用例按照测试功能进行拆分,分散到不同的测试文件中. ...

  7. map 插入数据的方式局别

    #include<map> #include<iostream> usingnamespace std; int main() { map <int, int> m ...

  8. 2017 ACM/ICPC Asia Regional Shenyang Online

    cable cable cable Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  9. BZOJ 1113 Wall ——计算几何

    凸包第一题. 自己认为自己写的是Andrew 其实就是xjb写出来居然过掉了测试. 刚开始把pi定义成了int,调了半天 #include <map> #include <cmath ...

  10. SPOJ GSS1 Can you answer these queries I ——线段树

    [题目分析] 线段树裸题. 注意update的操作,写结构体里好方便. 嗯,没了. [代码] #include <cstdio> #include <cstring> #inc ...