一、基类指针、派生类指针

父类指针可以new一个子类对象

二、虚函数

有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数?

有解决方案,这个对象指针必须是一个父类类型,我们如果想通过一个父类指针调用父类、子类中的同名函数的话,这个函数是有要求的;

在父类中,eat函数声明之前必须要加virtual声明eat()函数为虚函数。

一旦某个函数被声明为虚函数,那么所有派生类(子类)中eat()函数都是虚函数。

为了避免你在子类中写错虚函数,在C++11中,你可以在函数声明中增加一个override关键字,这个关键字用在子类中,而且是虚函数专用。

override就是用来说明派生类中的虚函数,你用了这个关键字之后,编译器就会认为你这个eat是覆盖了父类中的同名函数(只有虚函数才存在子类可以覆盖父类中同名函数的问题),那么编译器就会在父类中找同名的虚函数,如果没找到,编译器就会报错,如果你不小心在子类中把虚函数写错了名字,写错了参数,编译器能够帮你进行纠错。

final也是虚函数专用,用在父类中,如果我们在父类的函数声明中加了final,那么任何尝试覆盖在函数的操作都会引发错误。

调用虚函数执行的是“动态绑定”。动态表示我们程序运行的时候才能知道调用了那个子类的中的eat()虚函数。

动态的绑定到Men上去,还是Women上去,取决于new的Men还是Women;

动态绑定:运行的时候才决定你的phuman对象绑定到那个eat()函数上运行。

三、多态性

多态性只是针对虚函数来说的;

多态性:体现在具有继承关系的父类和子类之间,子类重新定义(重写)父类的成员函数eat(),同时父类把这个eat()函数声明为virtual虚函数;

通过父类的指针,只有到了程序运行时期,找到动态绑定到父类指针上的对象,这个对象有可能是某个子类对象,也可能是父类对象;

然后系统内部实际上是要查找一个虚函数表,找到函数eat()的入口地址,从而调用父类或子类的eat()函数,这就是运行时的多态性。

四、纯虚函数

纯虚函数是在基类中声明的函数,但是他在基类中没有定义,但是要求任何派生类都要定义该虚函数自己的实现方法;

基类中实现纯虚函数的方法使在函数原型后面增加 =0;

一旦一个类中又纯虚函数,那么你就不能生成这个类的对象了;

抽象类不能用来生成对象,主要目的是用来同意管理子类对象;

(1)纯虚函数的类叫做抽象类,不能用来生成该类对象,主要用于当做基类来生成子类用的;

(2)子类必须要实现该基类中定义纯虚函数;

五、基类的析构函数一般写成虚函数(虚析构函数)

用基类指针new子类的对象,在delete的时候,系统不会调用派生类的析构函数,存在问题;

解决方案:将基类的析构函数声明为虚析构函数;

在public继承中,基类对派生类及其对象的操作,只能影响那些从基类继承下来的成员,如果想要用基类对非继承的成员进行操作,则要把基类的这个函数定义为虚函数,析构函数也为虚函数,基类中的析构函数的虚属性也会被派生类继承,即派生类的析构函数也为虚函数。

Human这个类中的析构函数就要声明为virtual的,也就是说C++11中为了获得运行时多态,所调用的成员必须是virtual的。

如果一个类想要做基类,我们必须将类的析构函数声明为virtual虚函数;

只要基类中的析构函数为虚函数,就能保证我们delete基类指针时能够运行正确,不会出现内存泄漏。

虚函数会增加内存开销,类里面定义虚函数,编译器就会给这个类增加虚函数表,在这个表里存放虚函数的指针。

本节案例:

// Human.h
// 头文件防卫式声明
#ifndef __HUMAN__
#define __HUMAN__ #include "stdafx.h"
class Human
{
public:
Human();
virtual ~Human(); public:
virtual void eat();
virtual void eat2() = ;
}; #endif // Human.cpp
#include "stdafx.h"
#include "Human.h"
#include <iostream> Human::Human()
{
std::cout << "调用了Human::Human()" << std::endl;
} void Human::eat()
{
std::cout << "人类喜欢吃各种美食" << std::endl;
} Human::~Human()
{
std::cout << "调用了Human::~Human()" << std::endl;
} // Men.h
#ifndef __MEN__
#define __MEN__ #include "stdafx.h"
#include "Human.h" class Men : public Human
{
public:
Men();
~Men();
public:
virtual void eat() override;
virtual void eat2();
}; #endif // Men.cpp
#include "stdafx.h"
#include "Men.h"
#include <iostream> void Men::eat()
{
std::cout << "男人喜欢吃米饭" << std::endl;
} void Men::eat2()
{
std::cout << "男人喜欢吃米饭2" << std::endl;
} Men::Men()
{
std::cout << "调用了Men::Men()" << std::endl;
} Men::~Men()
{
std::cout << "调用了Men::~Men()" << std::endl;
} // Women.h
#ifndef __WOMEN__
#define __WOMEN__
#include "stdafx.h"
#include "Human.h"
class Women : public Human
{
public:
Women();
~Women(); public:
virtual void eat() override;
virtual void eat2() override;
}; #endif // Women.cpp
#include "stdafx.h"
#include "Women.h"
#include <iostream> Women::Women()
{
std::cout << "调用了Women::Women()" << std::endl;
} Women::~Women()
{
std::cout << "调用了Women::~Women()" << std::endl;
} void Women::eat()
{
std::cout << "女人喜欢吃面食" << std::endl;
} void Women::eat2()
{
std::cout << "女人喜欢吃面食2" << std::endl;
} // main.cpp
// Project3.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include "Human.h"
#include "Men.h"
#include "Women.h" using namespace std; int main()
{
Human *phuman = new Men;
phuman->eat(); // 男人喜欢吃米饭
delete phuman; phuman = new Women;
phuman->eat(); // 女人喜欢吃面食
delete phuman; //phuman = new Human;
//phuman->eat(); // 人类喜欢吃各种美食
//delete phuman;
phuman = new Men;
phuman->eat2(); // 男人喜欢吃米饭
delete phuman; phuman = new Women;
phuman->eat2(); // 女人喜欢吃面食
delete phuman; //Men men;
Men *pmen = new Men;
delete pmen; Human *phuman1 = new Men;
delete phuman1; // 没有执行子类的析构函数 return ;
}

C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构的更多相关文章

  1. 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?

    五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...

  2. 派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good

    大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸.我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree ...

  3. c++ 动态判断基类指针指向的子类类型(typeid)

    我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 本文提供了两种方法 ( ...

  4. C++ 基类指针,子类指针,多态

    基类指针和子类指针之间相互赋值(1)将子类指针赋值给基类指针时,不需要进行强制类型转换,C++编译器将自动进行类型转换.因为子类对象也是一个基类对象. (2)将基类指针赋值给子类指针时,需要进行强制类 ...

  5. C++ 基类指针和子类指针相互赋值

    首先,给出基类animal和子类fish [cpp] view plaincopy //======================================================== ...

  6. 当this指针成为指向之类的基类指针时,也能形成多态

    this指针: 1)对象中没有函数,只有成员变量 2)对象调用函数,通过this指针告诉函数是哪个对象自己谁. #include<iostream> using namespace std ...

  7. c++基类指针指向继承类调用继承类函数

      类里面重载运算符>>, 需要使用友元函数,而友元函数,不能作为虚函数. 所以,基类指针无法直接调用继承类里重构的 >>  ; 使用类转换,能解决掉,基类指针 调用 继承类 ...

  8. C++获取基类指针所指子类对象的类名

    我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 关键字 typeid, ...

  9. C++虚复制构造函数,设置Clone()方法返回基类指针,并设置为虚函数

    构造函数不能是虚函数.但有时候确实需要能传递一个指向基类对象的指针,并且有已创建的派生类对象的拷贝.通常在类内部创建一个Clone()方法,并设置为虚函数. //Listing 12.11 Virtu ...

随机推荐

  1. 创建一个子进程---vfork

    子.父进程共享数据段与堆栈段 函数原型:pid_t vfork(void) 返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1. 注意: vfork创建的进程是按先子进程后父进程的顺序执行的 ...

  2. 39 What Determines the Kind of Person You Are ?是什么决定了你是哪种内型的人 ?

    What Determines the Kind of Person You Are ?是什么决定了你是哪种内型的人 ? ①What determines the kind of person you ...

  3. Redis配置总结

    一:常用配置 1.bind,格式为bind 127.0.0.1:这个是很重要的配置,如果bind 127.0.0.1则外部网络是访问不了的(如果外部网络要访问还要开放端口) 2.port,格式为por ...

  4. python编码(一)

    下面介绍一下python的编码机制,unicode, utf-8, utf-16, GBK, GB2312,ISO-8859-1等编码之间的转换. 1.自动识别字符串编码: #coding:utf8 ...

  5. 笔记:PS 智能对象

    什么是智能对象? 智能对象是包含栅格或矢量图像(如 Photoshop 或 Illustrator 文件)中的图像数据的图层.智能对象将保留图像的源内容及其所有原始特性,从而让您能够对图层执行非破坏性 ...

  6. 【翻译】JavaScript循环和作用域

    我的翻译小站:https://www.zcfy.cc/article/javascript-loops-and-scope 翻译原文链接:https://flaviocopes.com/javascr ...

  7. opencv学习_4(opencv基础数据结构 CvPoint & CvSize & CvRect & CvScalar & CvArr & CvMat)

    1:包含在cxcore/include/cxtypes.h头文件中. 2:CvPoint系列   -----(x,y) CvPoint:表示图像中的点 CvPoint2D32f:二维空间中的点 CvP ...

  8. (原创)PetaPoco使用小记(2014-5-5更新)

    接触PetaPoco已经有一段时间了,为了全面了解一下PetaPoco,刚好结合目前在做的一个项目,对常用的几个业务操作用PetaPoco进行改写,如增删改查.分页以及存储过程的调用,在文章的最后附上 ...

  9. echarts呈现数据表图形

    讲一下echarts的用法,列举了两个图表,一个是单柱图,一个是多柱图,至于饼状图,只许更改echarts的类型就好了 一.首先是要两个div,用来存放两个图表 <div class=" ...

  10. python3--django for 循环中,获取序号

    功能需求:在前端页面中,for循环id会构不成连续的顺序号,所以要找到一种伪列的方式来根据数据量定义序号 因此就用到了在前端页面中的一个字段 forloop.counter,完美解决 <tbod ...