IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++速成日记(多态) -> 正文阅读

[C++知识库]c++速成日记(多态)

大家好,我是作者:隐匿269。今天我来为大家介绍多态(想要看例题,请点击https://blog.csdn.net/qq_60669466/article/details/122643830?spm=1001.2014.3001.5502。继承,多态和封装是c++另类的三个重点,封装,顾名思义就是封装数据,保护隐私。继承则是加深了类之间的练习。那么,多态又是什么呢,有什么样的作用,形式规则又是什么呢?阅读这篇文章,相信你会对多态有一个深入的印象。如果喜欢我的文章。记得点赞加关注。当然,如果有什么不解或发现了什么错误,欢迎在评论区发言。


  • 一、函数名联编

  • 二、多态

  • 三、纯虚函数

  • 四、final


一、函数名联编

1、什么是函数名联编

c++语言拥有函数重载,因此编译器在查看函数名的时候,还要查看参数,这种查看的过程,就称之为联编。用白话来说: 将源代码里的函数调用,解释成为执行特定的函数代码块的过程。(举例来说,就是主函数main调用了类里面的fun函数,运行fun函数里面的代码块的这一个过程)

2、动态联编、静态联编

一班来说,函数的编译都是在程序运行之前进行的。这样就导致了函数运行的时候,函数调用的代码块已经处于确定状态(静态联编)。而有时我们需要没有提前确定的函数,而是在运行的时候通过不同的类来确定函数对应的代码块(动态联编)。

静态联编(早期联编):

编译的阶段由编译器完成的联编

动态联编(又称晚期联编):

是程序进行的时候,进行的联编。

C++规定:动态联编是在虚函数的支持下实现的。

3、指针和引用的兼容性

父类可以指向子类(向上强制转换)

父类引用可以引用子类;

子类不能指向父类

子类引用不能引用父类;

class A
{
    public:
    int a;
    int b;
    A(){}
    A(int a,int b)
    {
		this->a=a;
    	this->b=b;
    }
}
class B
{
    public:
	B(int a,int b):public A(a,b)
    {
        
    }
}
int main(void)
{
    A a;
    B b(1,2);
    A *a1;
    B *b1(1,2);
    a1=&a;//类型匹配
    a1=&b;//类型不匹配,(向上强制转换)
    //父类可以指向子类
    &a1=b;
    //父类引用可以引用子类
    
    //b1=&a;错误
    b1=&a;//类型不匹配,(向下强制转换)
    //子类不能指向父类
    &b1=a;
    //子类引用不能引用父类
    
    b1=&b;//类型匹配
}

?为什么子类不能引用,指向父类?

父类派生出子类,子类包含父类,因此子类的数据比父类多。

通过父类指向(引用)子类,可以指出一部分数据(一般就是从父类中继承的数据和函数,会发生一定的变化)。

而子类引用(指向)父类,会导致子类中的内存无法完全储存,导致运用的时候出现错误。(例如子类中新添加了一个int数据,却无法通过父类初始化,里面的数据就会是个不确定数。)

4、实现多态联编的条件

  • ?必须把动态联编的行为定义为类的虚函数。?
  • 类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来。?
  • ?必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。

?二、多态

一、什么是多态

多种形态(状态)

同一个接口,通过不同的对象调用有着不同效果

二、虚函数(virtual)

本质就时重写

1、类中声明函数,成员函数前面加入virtual关键子

2、虚函数的作用

通过类的继承结合虚函数可以实现多态(动态联编)

3、特点

*虚函数表:类中有虚函数时,保存虚函数的首地址

(系统自动给予虚函数表:虚函数表的出现是因为有了虚函数,不会也不应该被继承)

*虚指针:指向虚函数表(4个字节的指针)

*虚函数表不会被继承(表里的东西(项)会被继承)

4、虚函数的限制:

  • 非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和也不能定义为虚函数,但可以将析构函数定义为虚函数。实际上,优秀的程序员常常把基类的析构函数定义为虚函数。因为,将基类的析构函数定义为虚函数后,当利用delete删除一个指向派生类定义的对象指针时,系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时,只调用基类的析构函数。?
  • 只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。?
  • 当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。?
  • 如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现这种同名函数。

三、实现多态

一、条件

1、先实现继承

2、继承之中,子类要从父类中继承一个功能不同的虚函数

3、父类指针或者引用父类或者子类对象,对着谁就调用谁的虚函数

二、示例

?

#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
    virtual void speak()
    {
        cout << "惹我光头强,熊熊变绵羊"<< endl;
    }
 ? ?virtual ~Animal(){}
private:
?
};
class Cow :public Animal
{
public:
    virtual void speak()
    {
        cout << "勇敢牛牛,不怕困难!" << endl;
    }
};
class Sheep :public Animal
{
public:
    virtual void speak()
    {
        cout << "你是没见过喜羊羊!" << endl;
    }
};
?
int main()
{
    Animal *PAnimal = new Sheep;
    PAnimal->speak();
    delete PAnimal;
    PAnimal = nullptr;
    system("pause");
    return 0;
}
/*
?释放时,会调用父类的析构函数。

(基类当中有虚函数的时候,并且是通过父类指针去分配子类对象的时候,在释放的时候,就只能通过父类指针去释放,低类的父类指针的时候,会调用父类指针的析构函数,不会调用子类的析构函数)

(把父类的析构函数,可以定义为虚析构。在释放父类指针的时候,也会调用子类的析构函数。)
*/
运行的结果会是:

?你是没见过喜羊羊!

而如果基类的speak函数没有加虚函数virtual?,运行的结果就会是:

惹我光头强,熊熊变绵羊。

(两者之间的差距,救是动态联编和静态联编之间的区别。前者在编译时确定代码块,可以清晰的发现对应的时子类函数。而后者,在编译之前就已经把主函数里面的speak函数与基类中的代码块一一对应。(编译之前,基类指针自然只会与自己类里面的函数代码块对应。而动态链接对应的则是拥有虚函数关键字的虚函数指针(指向虚函数表的首地址)。)

三、纯虚函数

一、概念

特殊的虚函数:

基类当中不实现函数体,给派生类实现。

写法:

virtual返回值类型 函数名 (参数表)=0;

析构函数也能写成纯虚函数。

class Animal
{
public:
    virtual void speak()=0;//纯虚函数
?
    virtual ~Animal()= 0;//纯虚析构函数
private:
?
};
Animal::~Animal()
{
    cout << "纯虚析构" << endl;
}

注意:纯虚析构必须要在类外实现一波。构造函数没有纯虚函数。

二、抽象类

1、定义

一个类中有一个及以上的纯虚函数。

2、特点

不能实例化对象,但是可以创建指针。

3、

派生类当中没有完全实现基类中所有的纯虚函数,那派生类也会变成抽象类。

四、final(c++11标准后出现)

父类的虚函数或者纯虚函数在子类中仍是虚函数。

不希望父亲的某个函数在子类中被重写。

class Animal
{
public:
    virtual void speak()=0;
?
    virtual ~Animal() {
        cout << "虚析构" << endl;
    }//虚析构函数
private:
?
};
?
class Cow :public Animal
{
public:
    virtual void speak() final
    {
        cout << "勇敢牛牛,不怕困难!" << endl;
    }
};
/*class M :public Cow
{
public:
    virtual void speak()//不能再重写,因为已经在其父类Cow中已经用final.
    {
?
    }
};*/
class A final
{
 ? ?public:
};
//不能在派生出派生类。对类本身没有任何影响
?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:24:21  更:2022-01-25 10:26:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:48:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码