虚函数与多态解析

1、什么是虚函数

   virtual 修饰的成员函数就是虚函数

#include<iostream>
class MM
{
public:
	virtual void print();
};
void MM::print()
{
	cout << "我是虚函数" << endl;
}

  以上定义了一个虚函数。我们需要提前知道一个空的类占用的是一个字节,在c语言中,不允许有空的结构体,但在c++中,允许有空的类。

class Boy
{
}

  在实际中占用一个字节,因为标识位需要一个字节。而如果一个类里面有一个虚函数,那么其内存会增加4。如果有两个虚函数,那么其内存大小和这个类只有一个虚函数的大小一模一样。

  因为这个4的本质内容是虚函数表指针其指向虚函数表,这个表里存放的全是指针。此处推荐这篇文章以供读者参考 https://blog.csdn.net/weixin_44302602/article/details/115227920

  如果有虚函数,那么一定会有虚函数表,所有虚函数都是共用的一个虚函数表指针。

  主函数如下

int main()
{
	MM mm; //实例化一个对象
	auto  p = &MM::print;
	//定义了函数指针
	typedef void(MM::*Func)();
	//这里定义了一个成员函数指针
	//就是把函数类型定义出来,就是换了个别名
	int** p = (int**)&mm;
	//指针类型强制转换	
	//此处为什么是二级指针,因为虚函数表按照内存看,其是一个二维数组
	Func pf = (Func)pp[0][0];
	pf();  //输出为我是虚函数
	return 0;
}

  在实际中,并不会这样调用虚函数。虚函数被继承下来后,其仍然是虚函数。

什么是纯虚函数

  纯虚函数就是没有函数体的虚函数(虚函数=0),其最大的作用为用来构造纯虚函数,至少一个纯虚函数的类叫做抽象类。抽象类无法构造对象。
  如:

class MM
{
public:
	virtual void print() = 0;
}

虚函数和多态

  多态: 同一种行为的不同结果。(因为初始化的对象不同导致的行为不同),多态的必要关系有三点:
  1.必须是公有继承
  2.必须存在virtual 类型
  3.必须存在不正常的指针引用
  多态需要理解的是在继承中,同名对象的访问问题

class MM
{
public:
	virtual void print()
	{
		cout << "MM::print" << endl;
	}
protected:
};

class Boy: public MM
{
public:
	void print()
	{
		cout << "Boy::print" << endl;
	}
protected:
};

int main()
{
	MM* pMM = new MM;
	pMM->print();//输出为MM::print
	Boy* pBoy = new Boy;
	pBoy->print();//输出为Boy::print
	//子类的方法将父类的方法覆盖掉,覆盖率父类的同名方法
	//除非:
	pBoy->MM:print();
	return 0;
}

  以上是正常的指针对象引用,即父类指针指向子类对象

MM* pMM;
pMM = new Boy();
pMM->print(); //输出为Boy:print
pMM = new MM();
pMM->print();//输出为MM::print

  如果子类指针用父类初始化,其不够安全,方法如下:

pBoy = dynamic_cast<Boy*>(new MM);
if(pBoy != nullptr)  pBoy->print();

抽象数据类型和析构函数

  对于抽象数据类型,下面是一个栈就是一个抽象数据类型。这段代码有问题,将会在下面指出。

class Stack
{
public:
	~Stack(){cout << "Stack delete" << endl;};
	virtual void push(int data) = 0;
	virtual void pop() = 0;
	virtual int top() const = 0;
	virtual bool empty() const = 0;
	virtual int size() const = 0;
};

  下面用数组实现栈:

class arrayStack: public Stack
{
public:
	~arrayStack(){cout << "arrayStack delete";}
};

  如果一个类继承了抽象类,子类没有完全重写父类的纯虚函数,那么其依然是一个抽象类。

  当存在一种不正常的赋值时,会出现释放问题:

Stack* pstack = new arrayStack;
delete pstack;
//输出为Stack delete

  此时并没有删除arrayStack,因此此时需要虚要虚析构函数。构造函数没有虚构造函数,但是有虚析构函数

dynamic_cast转化

  主要可以进行上行转换、下行转换和交叉交换。

class MM
{
public:
	MM(string name = "父类"):name (name){}
	virtual void print() { cout << name << endl;}
	string name;
};
class Son : public MM
{
public:
	Son(string name = "子类"): sname(name){}
	void print(){cout << sname << endl;}
	void printData(){cout << sname << endl;}
	string name;
};

  测试代码如下:

/*上行转换*/
MM* upStatic = static_cast<MM*>(new Son);
upStatic->print();//输出为子类
MM* upDynamic = dynamic_cast<MM*>(new Son);
upDynamic->print();//输出为子类
/*下行转换*/
Son* downStatic = static_cast<Son*>(new MM);
downStatic->print();
downStatic->printData();//会报错

  交叉转换是以父类为桥梁,实现子类与子类之间的转换。

class A
{
public:
	virtual void print() { cout << "A" << endl;}
};
class B
{
public:
	virtual void print() { cout << "B" << endl;}
};
class C: public A, public B
{
public:
	void print(){cout << "C" << endl;}
}

  测试代码

A* a = new C;
B* b = dynamic_cast<B*>(a);
b->print();//输出为C
return 0;

类中类的访问

class A
{
public:
	class B
	{
	public:
		void print();
	}
}

访问时应该这样做:类似于层层剥洋葱的方法:

A::B b;
b.print();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值