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! 啥东西呀,竟然遇上了,就探个究竟吧 ...
随机推荐
- TensorFlow使用记录 (八): 梯度修剪 和 Max-Norm Regularization
梯度修剪 梯度修剪主要避免训练梯度爆炸的问题,一般来说使用了 Batch Normalization 就不必要使用梯度修剪了,但还是有必要理解下实现的 In TensorFlow, the optim ...
- Java web 公文流转系统 完成结果
河北金力集团公文流转系统 1.项目需求: 河北金力集团是我省机械加工的龙头企业,主要从事矿山机械制造及各种机械零部件加工.企业有3个厂区,主厂区位于省高新技术开发区,3个分厂分别在保定.邢台和唐山.为 ...
- HDU 5818 Joint Stacks ——(栈的操作模拟,优先队列)
题意:有两个栈A和B,有3种操作:push,pop,merge.前两种都是栈的操作,最后一种表示的是如果“merge A B”,那么把B中的元素全部放到A中,且满足先入后出的栈原则. 分析:显然,我们 ...
- 3709: [PA2014]Bohater
3709: [PA2014]Bohater 或者:Bohater 题解 好狠啊这个题 z 要开 long long ,可能算掉血回血的时候会爆 long long 吧 首先把能回血的怪打死(不然你后面 ...
- 凸包Graham Scan算法实现
凸包算法实现点集合中搜索凸包顶点的功能,可以处理共线情况,可以输出共线点也可以不输出而只输出凸包顶点.经典的Graham Scan算法,点排序使用极角排序方式,并对共线情况做特殊处理.一般算法是将共线 ...
- windows 重启java进程脚本
这个脚本用于启动和重启javaWeb程序 @echo off rem 配置端口号 set port= rem 第一层循环检查端口占用的pid for /f "tokens=5" % ...
- Ironic 的 Rescue 救援模式实现流程
目录 文章目录 目录 救援模式 实现 UML 图 救援模式 以往只有虚拟机支持救援模式,裸机是不支持的.直到 Queen 版本 Ironic 实现了这个功能.救援模式下,用户可以完成修复.Troubl ...
- java最常见的5个错误
1. Null 的过度使用 避免过度使用 null 值是一个最佳实践.例如,更好的做法是让方法返回空的 array 或者 collection 而不是 null 值,因为这样可以防止程序抛出 Null ...
- on namespace ceilometer.$cmd failed: Authentication failed. 问题处理方案
on namespace ceilometer.$cmd failed: Authentication failed. UserNotFound: Could not find user ceilom ...
- redis-trib.rb创建Redis集群时失败报错解决方案
问题描述: [root@eshop-cache01 init.d]# redis-trib.rb create --replicas 1 192.168.1.110:7001 192.168.1.11 ...