多态-虚函数-虚表-虚析构函数

1.虚表 (x86环境)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.子类只重写一部分父类的虚函数的情况
在这里插入图片描述

3.为什么构造函数不能是虚函数
虚函数是通过虚函数表来实现的, 而虚函数表的地址(virtual table的指针)存储在对象空间。
所以如果构造函数是虚的,就需要通过虚表来调用, 可是对象还没有实例化(没有创建对象,就没有存放虚表指针的空间),无法找到vtable,所以构造函数不能是虚函数。
添加链接描述
添加链接描述

如果基类的析构函数不是虚函数,在delete p时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露。

#include "head.h"

/*
 * c++的多态通过虚函数 virtual function 来实现
 * 虚函数: 被virtual修饰的成员函数
 * 只要在父类中被声明位虚函数,子类中重写的函数也自动变成虚函数
 * 即子类中可以省略virtual关键字
 */

/*
 * 虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址
 * 所以虚表也叫虚函数表
 */
class Animal
{
public:
	Animal() {}
	~Animal() {}

	virtual void speak()
	{
		cout << "virtual Animal::Speak()" << endl;
	}

	virtual void run()
	{
		cout << "virtual Animal::Run()" << endl;
	}

	void testAnimal()
	{
		cout << "Animal::testAnimal()" << endl;
	}

public:
	int m_animal;
private:

};

class Cat : public Animal
{
public:
	Cat() {}
	~Cat() {}

	//void speak()
	//{
	//	cout << " Cat::Speak()" << endl;
	//}

	void run()
	{
		cout << " Cat::Run()" << endl;
	}

	void testCat()
	{
		cout << "Cat::testCat()" << endl;
	}
public:
	int m_cat;
};


//1.类中声明了虚函数后,其内存大小增加4,用来存储对象的地址
void test1()
{
	//有虚函数大小为8
	Animal anim;
	cout << sizeof(anim) << endl;

	//继承父类,有虚函数大小为12
	Cat cat;
	cout << sizeof(cat) << endl;
}

//2.所有的cat对象(不管在全局区,栈区,堆)公用一份虚表

//3.子类指针与父类指针
void test3()
{
	Cat* p1 = new Cat();
	p1->testAnimal(); /* Animal::testAnimal() */
	p1->testCat();    /* Cat::testCat()*/
	p1->speak();      /* Cat::Speak() */
	p1->m_animal = 10;
	p1->m_cat = 20;
	delete p1;

	cout << "---" << endl;

	//子类指针指向父类对象
	Cat* p2 = (Cat*) new Animal();
	p2->testAnimal();       //Animal::testAnimal()
	p2->testCat();          //Cat::testCat()
	
	p2->m_animal = 10;
	//p2->m_cat = 20;		//不安全,申请的堆空间没有m_cat内存

	p2->speak();			//virtual Animal::Speak()

	delete p2;
	cout << "---" << endl;
	//父类指针指向子类对象
	Animal* p3 =  new Cat();
	p3->testAnimal();
	//p3->testCat();		//err,在Animal类中找不到成员

	p3->m_animal = 10;
	//p3->m_cat = 20;		//err,在Animal类中找不到成员

	p3->speak();			//Cat::Speak()

	delete p3;
}

/*4.虚函数的实现原理是虚表,
*  这个虚表里面存储着最终需要调用的虚函数地址,虚表也叫虚函数表
*/
void test4()
{
	/*所有的Cat对象(不管在全局区、栈、堆)共用同一份虚表*/
	Animal* pCat = new Cat();
	pCat->m_animal = 20;
	
	/*
	* 虚表分析:
	* 1. 拿到pcat指针变量里面存储的地址值,即cat对象的地址值
	* 2. 取出cat对象的前面4个字节, 即虚表的地址
	*   (后面的地址依次是Cat类成员变量m_animal, m_cat的地址,值分别为20,0)
	* 3. 取出虚表中的前面4个字节,即Cat::speak()函数的地址
	* 4. 取出虚表中再之后的4字节,即Cat::run()函数的地址
	*/
	pCat->speak();	/* Cat::Speak() */
	pCat->run();	/* Cat::Run() */

	/* 假如在子类中只重写了父类的一个虚函数
	*  比如只重写了run(), 
	*  speak()的调用依然需要通过虚表来获取speak函数地址
	*  pCat->speak();	// virtual Animal::Speak() 
	*  pCat->run(); 	// Cat::Run() 
	*/

	delete pCat;
}

/*5. 子类调用父类成员函数
* 场景:子类的虚函数有重写父类函数,但是还想保留父类函数的功能
* 可以通过重写父类函数的同时,直接在函数里面调用父类的函数
*/
class A 
{
public:
	virtual void func()
	{
		cout << "A::func()" << endl;
	}
};
class AB : public A
{
public:
	virtual void func()
	{
		A::func();
		cout << "AB::func()" << endl;
	}

};
void test5()
{
	A* p = new AB();
	p->func(); // A::func() 	AB::func()
}


//6.纯虚函数, 抽象类(含有纯虚函数的类)
/*
* 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
* 抽象类(Abstract Class)
* 不可以实例化(不可以创建对象)
* 抽象类也可以包含非纯虚函数、成员变量
* 如果父类是抽象类,子类没有完全重写纯虚函数,那么这个子类依然是抽象类
*/
class Abstract
{
public:
	virtual void func1() = 0;
	virtual void func2() = 0;
};

class A_B : public Abstract
{
	void func1() {}
	//void func2() {}
};
void test6()
{
	//1.抽象类不能实例化
	//Abstract a; //err
	//Abstract* p = new Abstract(); //err
	//2.
	//A_B ab; //err, 子类只有全部重写父类的纯虚函数,自己才不是抽象类
}

/*7.纯析构函数
* 如果存在父类指针指向子类对象的情况,
* 应该将父类的析构函数声明为虚函数(虚析构函数)
* delete父类指针时,才会调用子类的析构函数,保证析构的完整性
*/
class Person
{
public:
	virtual void run()
	{
		cout << "Person::run()" << endl;
	}

	//虚析构函数,保证析构的完整性
	//否则析构时候只会看指针的类型,不会去看赋值的对象
	virtual ~Person()
	{
		cout << "Person:: ~Person()" << endl;
	}
};
class Student: public Person
{
public:
	void run()
	{
		cout << "Student::run()" << endl;
	}

	~Student()
	{
		cout << "Student:: ~Student()" << endl;
	}
};

void test7()
{
	Person* p = new Student();
	p->run();
	delete p;
}

int main()
{
	
	test7(); //析构完全:Student::~Student(), Person::~Person()

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值