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++学习 之 类的继承中的虚函数(笔记)的更多相关文章

  1. 谈谈c++中继承中的虚函数

      c++继承中的虚函数 c++是一种面向对象的编程语言的一个很明显的体现就是对继承机制的支持,c++中继承分很多种,按不同的分类有不同分类方法,比如可以按照基类的个数分为多继承和单继承,可以按照访问 ...

  2. 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数

    关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...

  3. 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数

    1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...

  4. C++构造与析构函数中调用虚函数的问题

    前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#if..#end,所以想利用C++的多态性,但是怎么让内存池完好退出却没想到自认为完美的方案.但是一个很偶然 ...

  5. C#中的虚函数及继承关系

    转载:http://blog.csdn.net/suncherrydream/article/details/8423991 若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法. 虚 ...

  6. [GeekBand] C++继承关系下虚函数内存分布

    本文参考文献:GeekBand课堂内容,授课老师:侯捷 :深度探索C++对象模型(侯捷译) :网络资料,如:http://blog.csdn.net/sanfengshou/article/detai ...

  7. C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现

    tfref 前言 C++对象的内存布局 只有数据成员的对象 没有虚函数的对象 拥有仅一个虚函数的对象 拥有多个虚函数的对象 单继承且本身不存在虚函数的继承类的内存布局 本身不存在虚函数(不严谨)但存在 ...

  8. 关于在C#中构造函数中调用虚函数的问题

    在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...

  9. C++-不要在构造和析构函数中调用虚函数

    在实习的单位搞CxImage库时不知为什么在Debug时没有问题,但是Release版里竟然跳出个Pure virtual function call error! 啥东西呀,竟然遇上了,就探个究竟吧 ...

随机推荐

  1. POI2012 BEZ-Minimalist Security | noi.ac #537 Graph

    题目链接:戳我 首先注意这张图有可能不连通!! 然后我们考虑对于每一个联通块,首先任意确定一个点,给它设最终值为x,然后进行搜索.(因为对于一个联通块而言,我们知道一个点的最终值,那么整个联通块上面点 ...

  2. JavaWeb-SpringBoot_(下)腾讯云点播服务之视频的显示-demo

    腾讯视频云点播 传送门 项目在腾讯云点播服务之视频的上传(上)[附源码]的基础上添加了两个html页面 此视频  播放传送门 (播放视频GIF会超过10M...) package com.Gary.v ...

  3. ETL-拉链算法-带删除的拉链算法

    truncate table CUST;truncate table TAG_CUST; truncate table vt_inc;truncate table vt_new; insert int ...

  4. hadoop hdfs hbase优化实例

    需求描述: 从hdfs中获取数据,字段url需要计算出url_type 通过进行hive的left outer join ,效率非常低.故将url的类型导入到hbase中,利用hbase快速查询的特点 ...

  5. [POJ1151][HDU1542]Atlantis(线段树,扫描线)

    英文题面,我就只放个传送门了. Solution  题意是算矩形面积并,这是扫描线算法能解决的经典问题. 算法的大致思想是,把每一个矩形拆成上边和下边(以下称作扫描线),每条扫描线有四个参数l,r,h ...

  6. webservice代码编写主要包括服务器端发布和客户端调用。

    一.java工程发布,java工程调用   (一).服务器端的编写 1.在eclipse里新建java project工程,创建功能类,通过关键字@webservice(name="newI ...

  7. 07.斐波那契数列 Java

    题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n<=39 思路 递归 若n<=2;返回n; 否则,返回Fibonacci ...

  8. websocket聊天体验

    light-example-4j/websocket目录有client-to-server.peer-to-peer两个示例项目,解决了我的两个问题:在线聊天.日志查看. 在线聊天,后续可以支持:最近 ...

  9. rocketmq 以广播方式实现消费者消费消息

    package com.bfxy.rocketmq.model; import java.util.List; import org.apache.rocketmq.client.consumer.D ...

  10. SQL编写自定义函数

    -- 通过一个子级ID 返回一级分类名称alter function calcclass(@dclassid as int)returns varchar(50)asbegin-- 通过一个子级ID ...