C++学习 之 类的继承中的虚函数(笔记)
1.多态行为
多态是面向对象语言的一种特征,让我们能够以类似的方式处理不同类型的对象。在C++中我们可以通过继承层次结构实现子类型多态。
我们可以通过下面的代码进一步了解多态:
#include<iostream>
using namespace std;
class Fish
{
public:
void FishSwim()
{
cout << "Fish swim in..." << endl;
}
}; class Tuna :public Fish
{
public:
void FishSwim()
{
cout << "Tuna swim in..." << endl;
}
}; void MakeFishSwim(Fish &InputFish)
{
InputFish.FishSwim();
}
int main()
{
Tuna myDinner;
myDinner.FishSwim();
MakeFishSwim(myDinner); return ;
}
从上面代码的运行结果来看我们定义了Tuna类并继承了Fish类的同名方法FishSwim;我们在函数MakeFishSwim中调用FishSwim并希望调用的是Tuna类中的该方法,但结果却调用了Fish类中的(因为在函数MakeFishSwim的参数是Fish类的)。尽管MakeFishSwim的参数类型是Fish类,为了能够完成Tuna类对象调用就执行Tuna类的方法Fish类的对象调用就执行Fish类的方法的多态行为,我们引入虚函数。
2.使用虚函数实现多态行为
虚函数实现多态行为代码示例:
#include<iostream>
using namespace std;
class Fish
{
public:
virtual void FishSwim()
{
cout << "Fish swim in..." << endl;
}
}; class Tuna :public Fish
{
public:
void FishSwim()
{
cout << "Tuna swim in..." << endl;
}
}; class Carp :public Fish
{
public:
void FishSwim()
{
cout << "Carp swim in..." << endl;
}
}; void MakeFishSwim(Fish &InputFish)
{
InputFish.FishSwim();
}
int main()
{
Fish myFish;
Tuna myDinner;
Carp myLunch; MakeFishSwim(myFish);
MakeFishSwim(myDinner);
MakeFishSwim(myLunch); return ;
}
由上面的代码和执行结果可以得出:引入虚函数实现了Fish类的对象调用MakeFishSwim函数就执行Fish类的方法、Tuna类对象调用就执行Tuna类的方法、Carp类的对象调用就执行Carp类的方法的多态行为。
3.虚构造函数
当某个函数为基类指针参数,其派生类的指针对象调用该函数并在该函数中使用delete释放使用new为派生类的指针对象分配的空间时,便可能导致内存泄漏、资源未释放等问题。
我们可以采用基类虚析构函数来实现调用派生类对象本身的析构函数,代码如下:
#include<iostream>
using namespace std;
class Fish
{
public:
Fish()
{
cout << "construct Fish"<< endl;
}
virtual ~Fish()
{
cout << "destroy Fish" << endl<<endl;
}
}; class Tuna :public Fish
{
public:
Tuna()
{
cout << "construct Tuna" << endl<<endl;
}
~Tuna()
{
cout << "destroy Tuna" << endl;
}
}; void DeleteFishSwim(Fish *InputFish)
{ delete InputFish;
}
int main()
{
cout << "create:"<<endl;
Tuna* myDinner = new Tuna;
cout << "delete:" << endl;
DeleteFishSwim(myDinner);
cout << "create:"<<endl;
Tuna Dinner;
cout << "delete:"<<endl;
return ;
}
当我们把基类Fish里的析构函数前去掉虚函数关键字virtual,就会发现结果中使用delete释放*myDinner所指向的空间时只调用了Fish的析构函数(因为函数DeleteFishSwim认为InputFish为基类Fish的对象)。这样便会导致Tuna对象*myDinner所指向的空间中属于Tuna类的内存块部分没有被释放,所以我们在这种情况下需要采用虚析构函数。
4.抽象基类和虚函数
虚函数的声明语法类似如下:
class Base
{
public:
virtual void DoSomething()=;
};
class Drived:public Base
{
public:
void DoSomething()
{
cout<<"virtual function"<<endl;
}
};
语法中"virtual void DoSomething()=0;"纯虚函数的声明语句。我们在基类中声明了纯虚函数DoSomething后,就要求其每个派生类中必须有DoSomething的实现。抽象基类使得我们能够声明每个派生类都必须实现的函数。
5.虚继承解决继承中菱形问题
继承中菱形问题:Animal类派生出Mammal、Bird、Reptile类;Platypus类由Mammal、Bird、Reptile派生出来;这便形成了继承中的菱形问题。Platypus类继承了Mammal类、Bird类、Reptile类;如果我们在Mammal类、Bird类、Reptile类继承Animal类时没有声明为虚继承时就会导致Mammal类、Bird类、Reptile类分别继承一个属于自己的Animal类(关系为非菱形);如果我们在Mammal类、Bird类、Reptile类继承Animal类时声明为虚继承时就会使得Mammal类、Bird类、Reptile类继承了同一个Animal类(关系为菱形)。
非菱形关系继承:
#include<iostream>
using namespace std;
class Animal
{
public:
int Age=;
Animal()
{
cout << "Animal constructor" << endl;
}
}; class Mammal : public Animal
{
}; class Bird : public Animal
{
}; class Reptile : public Animal
{
};
class Platypus : public Mammal, public Bird, public Reptile
{
public:
Platypus()
{
cout << "Platypus constructor" << endl;
}
}; int main()
{
Platypus duckBilledP;
//duckBilledP.Age = 25;
return ;
}
非菱形关系继承不仅导致内存占用提升,还使得Platypus的对象存在三个Age值,要想访问Platypus的Age值要使用"::"说明是从Mammal类、Bird类、Reptile类中的哪个类继承的Age(如果去掉main函数里的注释符号,会有编译错误)。实际上Platypus拥有三个Age值一般也是我们不想看到的。
菱形关系继承:
#include<iostream>
using namespace std;
class Animal
{
public:
int Age=;
Animal()
{
cout << "Animal constructor" << endl;
}
}; class Mammal :virtual public Animal
{
}; class Bird :virtual public Animal
{
}; class Reptile :virtual public Animal
{
};
class Platypus :public Mammal, public Bird, public Reptile
{
public:
Platypus()
{
cout << "Platypus constructor" << endl;
}
}; int main()
{
Platypus duckBilledP;
duckBilledP.Age = ;
return ;
}
菱形关系继承可以使得Platypus从Animal类中继承唯一的Age值。
C++学习 之 类的继承中的虚函数(笔记)的更多相关文章
- 谈谈c++中继承中的虚函数
c++继承中的虚函数 c++是一种面向对象的编程语言的一个很明显的体现就是对继承机制的支持,c++中继承分很多种,按不同的分类有不同分类方法,比如可以按照基类的个数分为多继承和单继承,可以按照访问 ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数
1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...
- C++构造与析构函数中调用虚函数的问题
前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#if..#end,所以想利用C++的多态性,但是怎么让内存池完好退出却没想到自认为完美的方案.但是一个很偶然 ...
- C#中的虚函数及继承关系
转载:http://blog.csdn.net/suncherrydream/article/details/8423991 若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法. 虚 ...
- [GeekBand] C++继承关系下虚函数内存分布
本文参考文献:GeekBand课堂内容,授课老师:侯捷 :深度探索C++对象模型(侯捷译) :网络资料,如:http://blog.csdn.net/sanfengshou/article/detai ...
- C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现
tfref 前言 C++对象的内存布局 只有数据成员的对象 没有虚函数的对象 拥有仅一个虚函数的对象 拥有多个虚函数的对象 单继承且本身不存在虚函数的继承类的内存布局 本身不存在虚函数(不严谨)但存在 ...
- 关于在C#中构造函数中调用虚函数的问题
在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...
- C++-不要在构造和析构函数中调用虚函数
在实习的单位搞CxImage库时不知为什么在Debug时没有问题,但是Release版里竟然跳出个Pure virtual function call error! 啥东西呀,竟然遇上了,就探个究竟吧 ...
随机推荐
- vue 甘特图简单制作
甘特图(Gantt chart)又称为横道图.条状图(Bar chart).其通过条状图来显示项目,进度,和其他时间相关的系统进展的内在关系随着时间进展的情况.以提出者亨利·L·甘特(Henrry L ...
- 动态DP教程
目录 前言 开始 更进一步 前言 最后一届NOIPTG的day2T3对于动态DP的普及起到了巨大的作用.然而我到现在还不会 开始 SP1716 GSS3 - Can you answer these ...
- Spring Boot教程(三十四)使用Redis数据库(2)
除了String类型,实战中我们还经常会在Redis中存储对象,这时候我们就会想是否可以使用类似RedisTemplate<String, User>来初始化并进行操作.但是Spring ...
- Bzoj3073Journeys
这不裸的dij吗?来,弄他. 打完以后发现不妙,这数据范围略神奇……算一算,考一场都可能跑不出来.map去重边(成功额外引入log)不怕,交.TLE,54. 这不玩呢吗,把map去了,交.MLE,71 ...
- MySQL datetime 和 timestamp 的区别
[转载]:MySQL中有关TIMESTAMP和DATETIME的总结 1. datetime 和 timestamp 的相同点 两者都可以用来表示YYYY-MM-DD HH:MM:SS[.fracti ...
- Python tuple 元组
Python 元组 Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 如下实例: tup1 ...
- Spring Boot学习笔记(1)
@SpringBootApplication用于注解Spring启动类,如下所示 @SpringBootApplication public class Application { public st ...
- laravel insert 、save、update、create区别(总结二)
1.insert:插入数据时,需要维护 created_at 和 updated_at字段, 2.save:无论插入或者更新,会自动维护,无需手动操作 //插入: public function st ...
- 解剖PTP协议
一. PTP是什么? 英文为Precise Time Protocol,翻译过来就是精确时间协议 二. 为何会出现PTP? 大家想必是知道NTP的存在,然而NTP的精度很低,只能达到毫秒级别的精度,那 ...
- 最长公共子子串 java
package maxCommon; /** * 找到最长公共子串 * @author root */ public class MaxCommonUnSeries { public static v ...