C++虚函数和多态超详细分析

目录
  • 1.什么是虚函数
  • 2.纯虚函数
  • 3.c++多态
  • 4.纯虚函数和ADT过程
  • 5.虚析构函数
  • 6.dynamic_cast类型转换
  • 7.成员函数指针

1.什么是虚函数

C++类中用virtual修饰的函数叫做虚函数,构造函数没有虚构造函数,存在虚析构函数,C++所有虚函数都是一个指针去存储的,所以具有虚函数的类,内存会增加一个指针大小的内存

#include<iostream>
#include<string>
using namespace std;
class MM
{
public:
	//虚函数指针是同一个,所以无论该类中有多少个虚函数,
	//占用的内存就只是一个指针的大小
	virtual void print()
	{
		cout << "我是第一个虚函数" << endl;
	}
	virtual void printData()
	{
		cout << "我是第二个虚函数" << endl;
	}
protected:
};
class son:public MM
{
public:
	void print()
	{
		//该函数也是虚函数,
        //父类中同名函数是虚函数
		//所以子类该函数也是虚函数
	}
protected:
};
int main()
{
	cout << sizeof(MM) << endl;
	cout << "...................." << endl;
	//虚函数表的理解,下面带图解
	MM mm;
	long long** p = (long long**)&mm;
	using Func = void(*)();
	Func f1 = (Func)p[0][0];
	Func f2 = (Func)p[0][1];
	f1();
	f2();
	return 0;
}

2.纯虚函数

纯虚函数也是虚函数的一种,只是没有函数体,下面请看怎么表示没有函数体

纯虚函数——>虚函数=0;

具有一个或者多个虚函数的类叫做抽象类

  • 抽象类不能构建对象
  • 但是可以构建函数指针
#include<iostream>
using namespace std;
class MM
{
public:
	virtual void print() = 0;
};
int main()
{
	//MM mm; //抽象类不能构建对象
	MM* pmm = nullptr;
	return 0;
}

3.c++多态

多态指的是因为指针的不同的赋值操作所导致的同一行为的不同结果。多态的这个概念东西不太重要,重要的是什么样的情况调用什么样的行为

  • 正常指针和正常对象调用行为,就近原则
  • 父类指针被子类对象初始化
  • 函数有virtual看对象类型
  • 函数没有virtual看指针类型
  • final关键字 禁止子类重写父类方法
  • override显示说明当前函数是重写函数

多态三个必要性条件:

  • 父类中存在虚函数
  • 存在不正常指针的引用(不正常赋值关系)
  • public继承
#include<iostream>
using namespace std;
class MM
{
public:
	void print()
	{
		cout << "父类" << endl;
	}
	virtual void printData()
	{
		cout << "MM" << endl;
	}
};
class son :public MM
{
public:
	void print()
	{
		cout << "子类" << endl;
	}
	void printData()
	{
		cout << "son" << endl;
	}
};
int main()
{
	MM* pmm = new son;
	pmm->print();
	pmm->printData();
	return 0;
}

4.纯虚函数和ADT过程

ADT:abstract data type抽象数据类型,主要是通过继承抽象类中,子类必须要实现父类的抽象方法,子类才可以创建对象。

#include<iostream>
#include<string>
using namespace std;
class data
{
public:
	string name;
	int age;
};
class MM
{
public:
	virtual bool empty() = 0;
	virtual int size() = 0;
};
class List:public MM
{
public:
	bool empty()
	{
		return 0;
	}
	int size()
	{
		return NULL;
	}
};
int main()
{
	MM* pmm = new List;
	pmm->empty();
	pmm->size();
}

5.虚析构函数

virtual修饰的析构函数就是虚析构函数,当存在子类对象初始化父类指针的时候,父类析构函数就要是虚析构函数,否则只会释放父类,存在内存泄漏问题

#include<iostream>
using namespace std;
class MM
{
public:
	virtual ~MM()
	{
		cout << "父类" << endl;
	}
};
class son:public MM
{
public:
	~son()
	{
		cout << "子类" << endl;
	}
};
int main()
{
	MM* pmm = new son;
	delete pmm;
	return 0;
}

6.dynamic_cast类型转换

  • 上行转换 子类到父类 和staic_cast差不多
  • 下行转换 父类到子类 dynamic_cast更为安全
  • 交叉转换 多继承
#include<iostream>
using namespace std;
class MM
{
public:
	virtual void print() { cout << "MM" << endl; }
};
class son:public MM
{
public:
	void print() { cout << "son" << endl; }
};
class A
{
public:
	virtual void print(){}
};
class B
{
public:
	virtual void print() {}
};
class C:public A,public B
{
public:
	void print()
	{
		cout << "c" << endl;
	}
};
int main()
{
	MM* partent = new MM;
	son* Son = new son;
	//上行转换----->没有必要
	MM* p = static_cast<MM*>(Son);
	MM* pp = dynamic_cast<MM*>(Son);
	MM* ppp = Son;       //可以
	//下行转换
	son* x = dynamic_cast<son*>(partent); //安全,父类没有virtual会报错---->多态
	if(!x)
	{
		printf("error");
	}                           //下行转换不安全,不会申请成功
	A* a = new A;
	B* b = dynamic_cast<B*>(a);
	b->print();
	return 0;
}

7.成员函数指针

#include<iostream>
#include<functional>
using namespace std;
class MM
{
public:
	void print() { cout << "king" << endl; }
	static void printData() { cout << "MM" << endl; }
};
int main()
{
	void(*p)() = &MM::printData;
	p();
	//必须通过对象调用
	void(MM::*pp)() = &MM::print;
	MM mm;
	(mm.*pp)();
	//函数适配器,让函数调用形态可以适用于别的形态
	auto f = bind(&MM::print, &mm);
	f();
	return 0;
}

到此这篇关于C++虚函数和多态超详细分析的文章就介绍到这了,更多相关C++虚函数和多态内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 多态虚函数的底层原理深入理解

    目录 1 多态的基本概念 1.1 什么是多态? 1.2 怎么实现多态 2 虚函数的底层原理 1 多态的基本概念 1.1 什么是多态? 多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为,通常是父类调用子类的重写函数,在C++中就是 父类指针指向子类对象,此时父类指针的向下引用就可以实现多态 比如看下面的代码: class Animal { public: //虚函数 virtual void speak() { cout << "动物在说话" <<

  • C++中关于多态实现和使用方法

    目录 赋值兼容 实例 多态 静多态 动多态 格式 实例 override 纯虚函数 含有虚函数的析构函数 注意事项 RTTI typeid typecast 多态实现 虚函数 一般继承(no override) 一般继承(override) 过程推断 都说 C++ 是面向对象的语言,其中的面向对象主要包括三部分:继承,封装,多态.继承和封装我们之前就简单介绍过,这里主要对多态的使用方法做一个简单说明. 赋值兼容 赋值兼容说的是在使用基类对象的地方可以使用公有继承类的对象来代替.赋值兼容是一种默认

  • C++中的多态问题—理解虚函数表及多态实现原理

    目录 一.多态的概念 概念 构成条件 二.虚函数的重写 重写的定义 重写的特殊情况 override和final关键字 区分重写.重载.重定义 抽象类的概念 三.多态的实现原理 父类对象模型 补充:生成默认构造方法的场景 子类对象模型 多态的调用原理 多继承的虚函数表 四.继承与多态中的常见问题 总结 注:编译环境为VS 2022,指针大小为4字节 一.多态的概念 概念 多态,指完成某个行为,不同的对象去完成时会产生出不同的状态.如:定一个一Animal类,类中包含动物的叫声这种方法,分别定义D

  • C++多态特性之派生与虚函数与模板详细介绍

    目录 继承与派生 虚函数 父类代码如下 模板 函数模板 类模板 字符串 继承与派生 C ++ 是面向对象编程,那么只要面向对象,都会有多态.继承的特性.C++是如何实现继承的呢? 继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程.例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数. 在C++中,派生(Derive) 和继承是一个概念,只是站的角度不同.继承是儿子接收父亲的产业,派生是父亲把产业传承给儿子. 被继承的类称为父类或基类,继承的类称

  • C++多态的示例详解

    目录 案例一:计算器 案例要求 代码实现 运行效果 案例二:制作饮品 案例要求 代码实现 运行效果 案例三:电脑组装 案例要求 代码实现 运行效果 今天就以三个案例来把C++多态的内容结束.第一个案例就是用多态写一个计算器并实现简单的加减乘除操作:第二个案例就是一个饮品制作,主要就是抽象类重写方法:第三个是比较综合的电脑组装案例,正文会详细介绍:那么就开始上手操作吧! 案例一:计算器 案例要求 使用多态实现计算器的加减乘除操作 代码实现 class AbstractCalculator { pu

  • C++ 超详细分析多态的原理与实现

    目录 多态的定义及实现 多态的构成条件 虚函数重写 C++11的override和final 抽象类 多态的原理 虚函数表 动态绑定与静态绑定 单继承和多继承关系的虚函数表 单继承中的虚函数表 多继承中的虚函数表 常见问题 多态的定义及实现 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态. 比如买票这个行为,当普通人买票时,是全价买票:学生买票时,是半价买票:军人买票时是优先买票. 多态的构成条件 多态是在不同继承关系的类对象,去调用同一函数

  • C++超详细分析讲解内联函数

    目录 宏函数(带参数的宏)的缺点 inline修饰的函数就是内联函数 内联函数的特点 宏函数和内联函数的区别 宏函数(带参数的宏)的缺点 第一个问题:宏函数看起来像一个函数调用,但是会有隐藏一些难以发现的问题. 例如: #define FUN(x, y) (x * y) printf("%d", add(3, 3 + 2)) //3 * 3 + 2 = 11 以上情况可以通过加 “()” 解决: #define FUN(x, y) (x * y) printf("%d&quo

  • C++超详细分析type_traits

    目录 定义基础常量 基础类型判断 类型处理 类型选择 判断是否相同 tips 实现is_base_of 本篇文章旨在引导大家自行实现type_traits的基础代码. 模板编程不像常规的代码,可以有if-else这些流控制语句,我们需要充分利用模板.模板特例.类型转换等特性来实现编译期的一系列判断和类型转换. 定义基础常量 第一步,我们需要定义true和false两个常量,所有的type_traits都基于此.我们的目的就是要用一个模板类型来表示是非,其中的value正好是这两个值.之后我们更高

  • Redis对象与redisObject超详细分析源码层

    目录 一.对象 二.对象的类型及编码 redisObject 结构体 三.不同对象编码规则 四.redisObject结构各字段使用范例 4.1 类型检查(type字段) 4.2 多态命令的实现(encoding) 4.3 内存回收和共享对象(refcount) 4.4 对象的空转时长(lru) 五.对象在源码中的使用 5.1 字符串对象 5.1.1字符串对象创建 5.1.2 字符串对象编码 5.1.3 字符串对象解码 5.1.4 redis对象引用计数及自动清理 六.总结 以下内容是基于Red

  • Java超详细分析泛型与通配符

    目录 1.泛型 1.1泛型的用法 1.1.1泛型的概念 1.1.2泛型类 1.1.3类型推导 1.2裸类型 1.3擦除机制 1.3.1关于泛型数组 1.3.2泛型的编译与擦除 1.4泛型的上界 1.4.1泛型的上界 1.4.2特殊的泛型上界 1.4.3泛型方法 1.4.4类型推导 2.通配符 2.1通配符的概念 2.2通配符的上界 2.3通配符的下界 题外话: 泛型与通配符是Java语法中比较难懂的两个语法,学习泛型和通配符的主要目的是能够看懂源码,实际使用的不多. 1.泛型 1.1泛型的用法

  • C++ 超详细分析数据结构中的时间复杂度

    别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080Ti 整个64GB的内存,你眼瞅着你 4G的内存,洋垃圾的处理器,打开个PS都要冒烟的那种,来来来,你跟我说说能比吗? 所以简单来说

  • Java 继承与多态超详细梳理

    目录 一.继承 1.继承的概念 2.继承的语法 3.父类成员访问 (1)子类中访问父类的成员变量 (2)子类中访问父类的成员方法 4.super关键字 5.子类构造方法 6.super和this 7.代码块执行顺序 8.继承方式 9.final关键字 10.继承和组合 二.多态 1.向上转型 2.重写 3.多态 一.继承 1.继承的概念 继承机制:是面向对象程序设计是代码可以复用的最重要手段,允许程序员在保持原有类特性的基础上进行扩展,增加新的功能,产生的新类,成为派生类/子类.继承主要解决的问

  • Java 栈与队列超详细分析讲解

    目录 一.栈(Stack) 1.什么是栈? 2.栈的常见方法 3.自己实现一个栈(底层用一个数组实现) 二.队列(Queue) 1.什么是队列? 2.队列的常见方法 3.队列的实现(单链表实现) 4.循环队列 一.栈(Stack) 1.什么是栈? 栈其实就是一种数据结构 - 先进后出(先入栈的数据后出来,最先入栈的数据会被压入栈底) 什么是java虚拟机栈? java虚拟机栈只是JVM当中的一块内存,该内存一般用来存放 例如:局部变量当调用函数时,我们会为函数开辟一块内存,叫做 栈帧,在 jav

  • C++ 超详细分析数据结构中的时间复杂度

    目录 什么是时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 什么是时间复杂度和空间复杂度 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080T

  • 超详细分析C语言动态内存管理问题

    目录 一.为什么存在动态内存的分配 二.动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 三.常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 对同一块动态内存多次释放 3.5 动态开辟内存忘记释放(内存泄漏) 四.几个经典的笔试题 五.C/C++程序的内存开辟 六.柔性数组 6.1 柔性数组的特点 6.2 柔性数组的使用 6.3 柔性数组的优势 上期结束了[

随机推荐