多态--下

文章目录

概念

优先使用组合、而不是继承;

继承会破坏父类的封装、因为子类也可以调用到父类的函数;

子类必须实现父类的纯虚函数;

内联函数没有地址,他是直接在定义的地方展开。所以不能是虚函数、虚函数是要有地址的;

虚函数不能是static函数,因为没有this指针、等于说是全局的,虚表要的是对象里面的;

普通函数继承是实现继承,就是不去重写父类的函数体,直接用的;虚函数的继承目的就是要重写这个函数体,自改成自己想要的;

虚函数在编译阶段就生成了,并且存在代码段;

析构函数最好写成多态。多态就是指向谁调谁,这样子类就可以调用自己的析构函数

构造函数不能写成多态;因为对象中的虚表指针实在初始化列表时开始初始化的;

多态如何实现的指向谁调谁?

虚函数的地址存在虚函数表中,通过虚函数表来调用虚函数;

同类型的对象共用一个虚表;

子类不重写父类的虚函数,也是和父类共用同一张虚表;重写就不会共用虚表

例子
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
using namespace std;

class person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票" << endl;
	}
};

class student : public person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生票" << endl;
	}
};


void buy(person& b)
{
	b.BuyTicket();
}


int main()
{
	student s;
	person p;
	buy(s);
	buy(p);
	return 0;
}
分析

1、子类继承后,虚函数表是一样的,重写后就把虚函数表copy出来换一个地址,把新的内容覆盖了;

2、调用时传过去的参数累心是谁的就指向谁的虚函数表,s是student类型那就是用student里面的函数;

2、子类虚函数不重写会怎样

如果不重写,那父级和子级虚函数表的地址是相同的

含有虚函数类的大小是多少?

普通函数不占空间,虚函数有个指针、所以站指针的大小空间,32位的4字节、64位的8字节;

在类里面:虚函数占一个指针的大小,不管有多少个虚函数就只占一个指针的大小;对象里面只是存了一个指针指向这个虚表

虚表的大小才要看有几个虚函数;

多继承的子类,继承了几个父类就有几个父类的指针大小

如下就一个虚函数64位

计算方法和结构体其实一样,内存对齐

加了一个int变量,理应8+4 = 12,但是最小对齐数为8,类的大小是其成员最小对齐数的整数倍;所以是16;

虚函数地址

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
using namespace std;

class person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人票" << endl;
	}
private:
	int a;
};

class student : public person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生票" << endl;
	}
};


void buy(person& b)
{
	b.BuyTicket();
}


class base
{
public:
	virtual void func1()
	{
		cout << "base::func1" << endl;
	}
private:
	int _b = 1;
};



void func()
{
	person b1;
	printf("vftptr:%p\n", *(int*) & b1);

	int i = 0;
	int* p1 = &i;
	int* p2 = new int;
	const char* p3 = "sad";
	printf("栈变量:%p\n", p1);
	printf("堆变量:%p\n", p2);
	printf("代码段常量:%p\n", p3);
	printf("代码段函数地址:%p\n", &base::func1);

}


int main()
{
	student s;
	person p;
	buy(s);
	buy(p);
	int size = sizeof(s);
	cout << size << endl;
	func();
	return 0;
}

问题:虚函数存在哪?虚函数表存在哪?

答:虚函数和普通函数都存在代码段、虚函数表也存在代码段

普通的函数、函数名就是地址(首地址)

成员函数的地址要加取地址符号&,以及要指定属于哪个类,也就是这样 &类名::函数名

虚表地址

cpp 复制代码
class Base
{
public:
	virtual void fuc1(){cout << "fuc1" << endl;}
	virtual void fuc2(){cout << "fuc2" << endl;}
private:
	int a;
};

class derive : public Base
{
public:
	virtual void fuc1(){ cout << "fuc1111" << endl; }
	virtual void fuc3(){cout << "fuc3" << endl;}
};



typedef void(*VF_PTR)();//函数指针类型怎么写的
void PrintVFTable(VF_PTR* pTable)
{
	for(size_t i = 0; pTable[i] != 0; ++i)
	{
		printf("vfTable[%d]:%p->", i, pTable[i]);
		VF_PTR f = pTable[i];
		f();//运行函数
	}
	cout << endl;
}




int main()
{
	//student s;
	//person p;
	//buy(s);
	//buy(p);
	//int size = sizeof(s);
	//cout << size << endl;
	//func();

	derive a;
	Base b;
	PrintVFTable((VF_PTR*)(*(int*)&b));
	PrintVFTable((VF_PTR*)(*(int*)&a));

	return 0;
}

父类上面两行、子类为下面三行;

子类fuc1重写过了虚表地址就变了,没写fuc2但是继承了fuc2的虚表地址;

强转两次,(int*)是要取前4个字符,(VF_PTR*)强转函数指针

函数指针怎么写 void(*)()

type void(*a)(),这个a就代表函数指针了

多继承的子类的大小怎么计算?

继承几个父类就含有几个指针;

cpp 复制代码
class Base1
{
public:
	virtual void fuc1() { cout << "b1:fuc1" << endl; }
	virtual void fuc2() { cout << "b1:fuc2" << endl; }
private:
	int a1;
};


class Base2
{
public:
	virtual void fuc1() { cout << "b2:fuc1" << endl; }
	virtual void fuc2() { cout << "b2:fuc2" << endl; }
private:
	int a2;
};

class derive2 : public Base1, public Base2
{
	virtual void fuc1() { cout << "b2:fuc1" << endl; }
	virtual void fuc3() { cout << "b2:fuc2" << endl; }

private:
	int a3;
};


int main()
{
	//student s;
	//person p;
	//buy(s);
	//buy(p);
	//int size = sizeof(s);
	//cout << size << endl;
	//func();

	//derive a;
	//Base b;
	//PrintVFTable((VF_PTR*)(*(int*)&b));
	//PrintVFTable((VF_PTR*)(*(int*)&a));

	cout << sizeof(derive2) << endl;
	derive2 a1;
	PrintVFTable((VF_PTR*)(*(int*)&a1));
	PrintVFTable((VF_PTR*)(*(int*)((char*)&a1 + sizeof(Base1))));

	return 0;
}

继承两个父类,两个指针的大小;

不同父类的虚表继承后不会共用;

derive2成员函数fuc3的虚表和Base1的虚表共用了,随机共用一个父类的虚表;

练习题

1、

2、

类中就只有变量、按顺序往下排;

p3是指向整块区域,所以首地址也是b1存的首地址;

p1也是b1存的首地址;

p2是b2的首地址;

所以选c;

虚函数和虚继承

是两个完全不同的概念;

用的都是virtual;

虚继承

菱形继承时,B、C继承A,D继承BC;

如上图使用虚继承,就会把公共的变量_a,放到一个地址上;

如果不用虚继承的话,就会有三个_a的地址;造成数据的冗余性;

二义性也是不用虚继承造成的,就是再回去找_a时,有很多_a;

相关推荐
倔强的石头1062 分钟前
【C++指南】类和对象(九):内部类
开发语言·c++
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导1 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
唐诺9 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨9 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客10 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++