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! 啥东西呀,竟然遇上了,就探个究竟吧 ...
随机推荐
- websocket 连接测试端口服务是否正常代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- nu.random.seed()如何理解
结论: np.random.seed(a) # 按照规定的顺序生成随机数 # 参数a指定了随机数生成的起始位置: # 如果两处都采用了np.random.seed(a),且两处的参数a相同,则生成的随 ...
- 关于lower_bound()和upper_bound()
关于lower_bound()和upper_bound(): 参考:关于lower_bound( )和upper_bound( )的常见用法 注意:查找的数组必须要是排好序的.因为,它们查找的方式也是 ...
- Vue_(组件)实例方法
Vue.js实例方法/生命周期 传送门 常用的实例方法 数据: 传送门 vm.$set:设置属性值 vm.$delete:删除属性值 vm.$watch:观测数据变化 生命周期 vm.$mount:手 ...
- $\LaTeX$数学公式大全4
$4\ Standard\ Function\ Names$将英文转化为罗马文$\arccos$ \arccos$\cos$ \cos$\csc$ \csc$\exp$ \exp$\ker$ \ker ...
- 设置PyCharm中选择文本的背景颜色和代码中和选中单词相同单词的背景颜色
1 设置选中单词的背景颜色 首先进入File->Setting->Editor->Color Scheme后复制一个存在的颜色主题作为自定义的颜色主题(默认的颜色主题是无法修改的,也 ...
- fastadmin后台视频文件上传,受限制,修改php.ini配置即可
post_max_size = 50M(根据情况)upload_max_filesize = 50M(根据情况)
- java中线程状态
刚开始接触java时,就觉得多线程是一个障碍,不容易理解,当时选择了跳过,不过工作一段时间后,发现这块还是需要深入研究一下的,及时平时工作中不使用多线程,但一定会使用web容器,比如tomcat,也是 ...
- 十大经典排序算法最强总结(含JAVA代码实现)(转)
十大经典排序算法最强总结(含JAVA代码实现) 最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在“桶排序”算法中对每 ...
- python内存泄露memory leak排查记录
问题描述 A服务,是一个检测MGR集群主节点是否发生变化的服务,使用python语言实现的. 针对每个集群,主线程会创建一个子线程,并由子线程去检测.子线程会频繁的创建和销毁. 上线以后,由于经常会有 ...