C++多态

#include<iostream>
using namespace std;

#if 0
//多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当
//不同的对象去完成时会产生出不同 的状态

//在继承中要构成多态还有两个条件:
//1. 调用函数的对象必须是指针或者引用。
//2. 被调用的函数必须是虚函数,且完成了虚函数的重写。

//虚函数:就是在类的成员函数的前面加virtual关键字
//class Person
//{
//public:
//	virtual void BuyTicket()  //虚函数
//	{
//		cout << "买票-全价" << endl;
//	}
//};

//虚函数的重写:
//派生类中有一个跟基类的完全相同虚函数,
//我们就称子类的虚函数重写了基类的虚函数,完全相同是指:
//函数名、参数、返回值都相同。另外虚函数的重写也叫作虚函数的覆盖

//class Person
//{
//public:
//	virtual void BuyTicket() { cout << "买票-全价" << endl; }
//};
//class Student : public Person
//{
//public:
//	virtual void BuyTicket() { cout << "买票-半价" << endl; }
//};
//
//void Func(Person& p)
//{
//	p.BuyTicket();
//}
//
//int main()
//{
//	Person p;
//	Student s;
//
//	Func(p);
//	Func(s);
//
//}

//虚函数重写的例外:协变
//虚函数重写有一个例外:重写的虚函数的返回值可以不同,
//但是必须分别是基类指针和派生类指针或者基类 引用和派生类引用。
//协变我们了解一下就可以了,这个用得很少

//class A {};
//class B : public A {};
//class Person
//{
//public:
//	virtual A* fun() { return new A; }
//};
//class Student : public Person
//{
//public:
//	virtual B* fun() { return new B; }
//};


//不规范的重写行为 
//在派生类中重写的成员函数可以不加virtual关键字,也是构成重写的,
//因为继承后基类的虚函数被继承下来 了在派生类依旧保持虚函数属性,
//我们只是重写了它。但是这是非常不规范的,我们平时不要这样使用

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};
class Student : public Person
{
public:
	void BuyTicket()
	{
		cout << "买票-半价" << endl;
	}
};

void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person p;
	Student s;

	Func(p);
	Func(s);

	return 0;
}

#endif

#if 0

//析构函数的重写问题
//基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。
//这里他们的函数名不相同,看起来违背了重写的规则,其实不然
//,这里可以理解为编译器对析构函数的名称做了特殊处理,
//编译后析构函数的名称统一处理成destructor,
//这也说明的基类的析构函数最好写成虚函数。——>>这样就可以构成多态,
//解决内存泄漏问题

class Person
{
public:
	virtual ~Person()
	{
		cout << "~Person()" << endl;
	}
};
class Student : public Person
{
public:
	virtual ~Student()
	{
		cout << "~Student" << endl;
	}
};

int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;

	delete p1;
	delete p2;

	return 0;
}
#endif

//接口继承和实现继承
//普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,
//继承的是函数的实现。虚函数的 继承是一种接口继承,
//派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。
//所 以如果不实现多态,不要把函数定义成虚函数。


#if 0
//纯虚函数
//在成员函数的形参后面写上 = 0,则成员函数为纯虚函数。
//包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
//纯虚 函数在派生类中重新定义以后,派生类才能实例化出对象。

class Car
{
public:
	virtual void Drive() = 0;
};

class Benz : public Car
{
public:
	virtual void Drive()
	{
		cout << "Benz-舒适" << endl;
	}
};

class BWM : public Car
{
public:
	virtual void Drive()
	{
		cout << "BWM-操控" << endl;
	}
};

void Test()
{
	Car* pBenz = new Benz;
	pBenz->Drive();

	Car* pBWM = new BWM;
	pBWM->Drive();
}
int main()
{
	Test();

	return 0;
}
#endif


//C++11 override 和final修饰虚函数
//实际中我们建议多使用纯虚函数 + overrid的方式来强制重写虚函数,
//因为虚函数的意义就是实现多态,如果 没有重写,虚函数就没有意义

//1.final修饰父类的虚函数不能被子类重写
//class Car
//{
//public:
//	virtual void Drive()final
//	{}
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive() //无法重写
//	{
//		cout << "Benz-舒适" << endl;
//	}
//};
//
//void Test()
//{
//	Car* pBenz = new Benz; 
//}


//2.override 修饰子类虚函数强制完成重写,如果没有重写编译会报错
//class Car
//{
//public:
//	virtual void Drive() {}
//};
//class Benz : public Car
//{
//public:
//	virtual void Drive()override
//	{
//		cout << "Benz-舒适" << endl;
//	}
//};
//
//void Test()
//{
//	Car* pBenz = new Benz;
//	pBenz->Drive();
//}

//int main()
//{
//	Test();
//	return 0;
//}


#if 0
//面试题
//class Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Func1()" << endl;
//	}
//
//private:
//	int _b = 1;
//};

//针对上面的代码我们做出以下改造
// 1.我们增加一个派生类Derive去继承Base 
// 2.Derive中重写Func1 
// 3.Base再增加一个虚函数Func2和一个普通函数Func3 

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1" << endl;
	}

	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}

	void Fun3()
	{
		cout << "Func3()" << endl;
	}

private:
	int _b = 1;
};

class Drive : Base
{
public:
	virtual void Func1()
	{
		cout << "Drive::Func1()" << endl;
	}

private:
	int _d = 2;
};

int main()
{
	//cout << sizeof(Base) << endl;
	//Base b;
	//cout << sizeof(b);


	//cout << sizeof(Base) << endl;
	//cout << sizeof(Drive) << endl;
	//Base b;
	//Drive d;
	//cout << sizeof(b) << endl;
	//cout << sizeof(d);

	return 0;
}
//通过观察和测试,我们发现了以下几点问题:
//1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,
//一部分是父类继承下来的成员,虚表指针也就 是存在部分的另一部分是自己的成员。 

//2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,
//所以d的虚表中存的是重 写的Derive::Func1,所以虚函数的重写也叫作覆盖,
//覆盖就是指虚表中虚函数的覆盖。重写是语法的 叫法,覆盖是原理层的叫法。 

//3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了
//但是不是虚函数,所以不会 放进虚表。 

//4. 虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr。

//5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 

//b.如果派生类重写了基 类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 
//c.派生类自己新增加的虚函数按其在 派生类中的声明次序增加到派生类虚表的最后。

//6. 这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的?
//答:虚函数存在虚表,虚表 存在对象中。注意上面的回答的错的。
//但是很多童鞋都是这样深以为然的。注意虚表存的是虚函数指 针,不是虚函数,
//虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。
//另外 对象中存的不是虚表,存的是虚表指针。
//那么虚表存在哪的呢?实际我们去验证一下会发现vs下是存在 代码段的只读区.
//所以虚函数表一般情况存在只读代码段。
#endif

虚函数表

//多态的原理
//class Person
//{
//public:
//	virtual void BuyTicket()
//	{
//		cout << "买票-全价" << endl;
//	}
//};
//class Student : public Person
//{
//public:
//	virtual void BuyTicket()override
//	{
//		cout << "买票-半价" << endl;
//	}
//};
//void Func(Person* p)
//{
//	p->BuyTicket();
//}


//单继承中的虚基表

class Base
{
public:
	virtual void func1() { cout << "Base::func1()" << endl; }
	virtual void func2() { cout << "Base::func2()" << endl; }
private:
	int _b;
};

class Derive : public Base
{
public:
	virtual void func1() { cout << "Derive::func1()" << endl; }
	virtual void func3() { cout << "Derive::func3()" << endl; }
	virtual void func4() { cout << "Derive::func4()" << endl; }
private:
	int _d;
};

typedef void(*VFPTR)();  //函数指针
void PrintVTabe(VFPTR vTable[]) //参数是函数指针的指针(二级指针)
{
	// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
	cout << "虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf("第%d个虚函数地址: 0x%x,->", i, vTable[i]);
		vTable[i]();
		//VFPTR f = vTable[i];
		//f();
	}
	cout << endl;
}


int main()
{
	Base b;
	Derive d;

	VFPTR* vTableb = (VFPTR*)(*(int*) &b);
	PrintVTabe(vTableb);

	VFPTR* vTabled = (VFPTR*)(*(int*) &d);
	PrintVTabe(vTabled);

	return 0;
}

//虚基表是在菱形虚继承对象模型中:祖先类中的成员放在子孙类中的最后面。同时,两个父类中
//各自存了一个虚基表指针,该指针指向虚基表,虚基表中下一个地址存放的是一个找到祖先类中
//成员的偏移量。
//
//虚函数表:是存放虚函数指针的表。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值