C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构
一、基类指针、派生类指针
父类指针可以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++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构的更多相关文章
- 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?
五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...
- 派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good
大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸.我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree ...
- c++ 动态判断基类指针指向的子类类型(typeid)
我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 本文提供了两种方法 ( ...
- C++ 基类指针,子类指针,多态
基类指针和子类指针之间相互赋值(1)将子类指针赋值给基类指针时,不需要进行强制类型转换,C++编译器将自动进行类型转换.因为子类对象也是一个基类对象. (2)将基类指针赋值给子类指针时,需要进行强制类 ...
- C++ 基类指针和子类指针相互赋值
首先,给出基类animal和子类fish [cpp] view plaincopy //======================================================== ...
- 当this指针成为指向之类的基类指针时,也能形成多态
this指针: 1)对象中没有函数,只有成员变量 2)对象调用函数,通过this指针告诉函数是哪个对象自己谁. #include<iostream> using namespace std ...
- c++基类指针指向继承类调用继承类函数
类里面重载运算符>>, 需要使用友元函数,而友元函数,不能作为虚函数. 所以,基类指针无法直接调用继承类里重构的 >> ; 使用类转换,能解决掉,基类指针 调用 继承类 ...
- C++获取基类指针所指子类对象的类名
我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 关键字 typeid, ...
- C++虚复制构造函数,设置Clone()方法返回基类指针,并设置为虚函数
构造函数不能是虚函数.但有时候确实需要能传递一个指向基类对象的指针,并且有已创建的派生类对象的拷贝.通常在类内部创建一个Clone()方法,并设置为虚函数. //Listing 12.11 Virtu ...
随机推荐
- KBMMW 4.70.00 发布
We are happy to announce the release of kbmMW v. 4.70.00 Professional and Enterprise Edition. kbmMW ...
- ajax序列化表单,再也不用通过data去一个个的传值了
jQuery的serialize()方法通过序列化表单值,创建URL编码文本字符串,我们就可以选择一个或多个表单元素,也可以直接选择form将其序列化 这样,我们就可以把序列化的值传给ajax()作为 ...
- 2018.07.23 codeforces 438D. The Child and Sequence(线段树)
传送门 线段树维护区间取模,单点修改,区间求和. 这题老套路了,对一个数来说,每次取模至少让它减少一半,这样每次单点修改对时间复杂度的贡献就是一个log" role="presen ...
- springboot问题,没有主清单属性
在pom.xml中添加 <build> <plugins> <plugin> <groupId>org.apache.maven.plugins< ...
- 20155327 2016-2017-4 《Java程序设计》第6周学习总结
20155327 2016-2017-4 <Java程序设计>第6周学习总结 教材学习内容总结 理解流与IO 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两 ...
- (转) MVC 中 @help 用法
ASP.NET MVC 3支持一项名为“Razor”的新视图引擎选项(除了继续支持/加强现有的.aspx视图引擎外).当编写一个视图模板时,Razor将所需的字符和击键数减少到最小,并保证一个快速.通 ...
- hdu3853 LOOPS(概率dp) 2016-05-26 17:37 89人阅读 评论(0) 收藏
LOOPS Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 125536/65536 K (Java/Others) Total Su ...
- codeforces 678C. Joty and Chocolate(容斥) 2016-10-15 21:49 122人阅读 评论(0) 收藏
C. Joty and Chocolate time limit per test 1 second memory limit per test 256 megabytes input standar ...
- ASP.NET MVC学习之模型验证详解
ASP.NET MVC学习之模型验证篇 2014-05-28 11:36 by y-z-f, 6722 阅读, 13 评论, 收藏, 编辑 一.学习前的一句话 在这里要先感谢那些能够点开我随笔的博友们 ...
- SQL学习笔记1
2018.10.15:周一 -- 返回前5个数据 SELECT TOP 5 * FROM Student; -- 返回前50%的数据 SELECT TOP 50 PERCENT * FROM ...