C++基础知识(多态)

目录

1、静态联编和动态联编

2、多态原理解析

3、多态案例——计算器案例

4、抽象类和纯虚函数

5、纯虚函数和多继承

 6、向上类型转换和向下类型转换

 7、多态案例二——PK游戏需求


1、静态联编和动态联编
  • 多态分类

       静态多态:函数重载

       动态多态:虚函数  继承关系

  • 静态联编:地址早绑定 编译阶段绑定好地址
  • 动态联编:地址晚绑定 运行时候绑定好地址
  • 多态:父类的引用或者指针指向子类对象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};
 
class Cat :public Animal
{
	virtual void speak()  //virtual可写也可不写
	{
		cout << "小猫在说话" << endl;
	}
};
 
//调用doSpeak,speak函数的地址早就绑定好了,早绑定,静态联编  编译阶段就确定好了地址
//如果想调用猫的speak,不能提前绑定好函数的地址了,所以需要在运行时再去确定函数地址
//动态联编,写法doSpeak改为虚函数,在父类上声明虚函数,发生了多态
//父类的引用或者指针  指向  子类对象
void doSpeak(Animal & animal)  //Animal & animal = cat
{
	animal.speak();
}
//如果发生了继承的关系,编译器允许进行类型转换
void test01()
{
	Cat cat;
	doSpeak(cat);
}
 
int main()
{
	test01();
	system("pause");
	return 0;
}
2、多态原理解析
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};
 
class Cat :public Animal
{
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
};
 
//调用doSpeak,speak函数的地址早就绑定好了,早绑定,静态联编  编译阶段就确定好了地址
//如果想调用猫的speak,不能提前绑定好函数的地址了,所以需要在运行时再去确定函数地址
//动态联编,写法doSpeak改为虚函数,在父类上声明虚函数,发生了多态
//父类的引用或者指针  指向  子类对象
void doSpeak(Animal & animal)  //Animal & animal = cat
{
	animal.speak();
}
//如果发生了继承的关系,编译器允许进行类型转换
void test01()
{
	Cat cat;
	doSpeak(cat);
}
 
void test02()
{
	//cout << sizeof(Animal) << endl;
	//父类指针指向子类对象 多态
	Animal *animal = new Cat;
	//animal->speak();
	//*(int*)*(int*)animal 函数地址   *(int*)animal虚函数表
	((void(*)()) (*(int*)*(int*)animal))();
}
 
int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}

父类:Animal

子类:Cat

可以看出,子类将speak()重写为自己的,所以输出才会是“小猫在说话”。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
	virtual void eat()
	{
		cout << "动物在吃饭" << endl;
	}
};
 
class Cat :public Animal
{
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
	virtual void eat()
	{
		cout << "小猫在吃鱼" << endl;
	}
};
 
//调用doSpeak,speak函数的地址早就绑定好了,早绑定,静态联编  编译阶段就确定好了地址
//如果想调用猫的speak,不能提前绑定好函数的地址了,所以需要在运行时再去确定函数地址
//动态联编,写法doSpeak改为虚函数,在父类上声明虚函数,发生了多态
//父类的引用或者指针  指向  子类对象
void doSpeak(Animal & animal)  //Animal & animal = cat
{
	animal.speak();
}
//如果发生了继承的关系,编译器允许进行类型转换
void test01()
{
	Cat cat;
	doSpeak(cat);
}
 
void test02()
{
	//cout << sizeof(Animal) << endl;
	//父类指针指向子类对象 多态
	Animal *animal = new Cat;
	//animal->speak();
	//*(int*)*(int*)animal 函数地址   *(int*)animal虚函数表
	((void(*)()) (*(int*)*(int*)animal))();
	//猫吃鱼的地址 *((int*)*(int*)animal+1)
	((void(*)())(*((int*)*(int*)animal + 1)))();
}
 
int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}
3、多态案例——计算器案例
  • 早期方法 是不利于扩展
  • 开发有原则  开闭原则  --  对扩展开放  对修改关闭
  • 利用多态实现 – 利于后期扩展,结构性非常好,可读性高, 效率稍微低,发生多态内部结构复杂
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
//class Calculator
//{
//public:
//	void setv1(int v)
//	{
//		this->val1 = v;
//	}
//
//	void setv2(int v)
//	{
//		this->val2 = v;
//	}
//
//	int getResult(string oper)
//	{
//		if (oper == "+")
//		{
//			return val1 + val2;
//		}
//		else if (oper == "-")
//		{
//			return val1 - val2;
//		}
//	}
//
//private:
//	int val1;
//	int val2;
//};
//
//void test01()
//{
//	Calculator cal;
//	cal.setv1(10);
//	cal.setv2(10);
//	cout << cal.getResult("+") << endl;
//	cout << cal.getResult("-") << endl;
//}
 
//真正的开发中,有个开发原则,开闭原则
//对扩展开放,对修改关闭
 
//利用多态实现计算器
class abstractCalculator
{
public:
	virtual int getResult()
	{
		return 0;
	};
 
	void setv1(int v)
	{
		this->val1 = v;
	}
	
	void setv2(int v)
	{
		this->val2 = v;
	}
 
public:
	int val1;
	int val2;
};
 
//加法计算器
class PlusCalculator :public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 + val2;
	}
};
 
//减法计算器
class SubCalculator :public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 - val2;
	}
};
 
class MultiCalculator:public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 * val2;
	}
};
 
void test02()
{
	abstractCalculator *abc;//加法计算器
	abc = new PlusCalculator;
 
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	delete abc;
 
	abc = new SubCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	delete abc;
 
	abc = new MultiCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
}
 
int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}
4、抽象类和纯虚函数

       在设计时,常常希望基类仅仅作为其派生类的一个接口。这就是说,仅想对基类进行向上类型转换,使用它的接口,而不希望用户实际的创建一个基类的对象。同时创建一个纯虚函数允许接口中放置成员原函数,而不一定要提供一段可能对这个函数毫无意义的代码。

  • 纯虚函数写法  virtual void func() = 0;
  • 抽象类型
  • 抽象类 不可以实例化对象
  • 如果类 继承了抽象类, 必须重写抽象类中的纯虚函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
//真正的开发中,有个开发原则,开闭原则
//对扩展开放,对修改关闭
 
//利用多态实现计算器
class abstractCalculator
{
public:
	//虚函数
	/*virtual int getResult()
	{
		return 0;
	};*/
	//纯虚函数
	//如果父类中有了纯虚函数,那么子类继承父类则必须实现纯虚函数
	//如果父类中 有了纯虚函数,这个父类 就无法实例化对象了
	//这个类有了纯虚函数,通常又称为 抽象类
	virtual int getResult() = 0;
	
	void setv1(int v)
	{
		this->val1 = v;
	}
 
	void setv2(int v)
	{
		this->val2 = v;
	}
 
public:
	int val1;
	int val2;
};
 
class A :public abstractCalculator
{
 
 
};
 
 
 
//加法计算器
class PlusCalculator :public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 + val2;
	}
};
 
//减法计算器
class SubCalculator :public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 - val2;
	}
};
 
class MultiCalculator :public abstractCalculator
{
public:
	virtual int getResult()
	{
		return val1 * val2;
	}
};
 
void test02()
{
	abstractCalculator *abc;//加法计算器
	abc = new PlusCalculator;
 
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	delete abc;
 
	abc = new SubCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	delete abc;
 
	abc = new MultiCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
 
	//A a;  不允许使用抽象类的对象,因为在A中没有实现父类的纯虚函数
	//如果父类有了纯虚函数,这个父类就无法实例化对象
	//abstractCalculator *ab = new abstractCalculator; 
	//abstractCalculator aaa;
}
 
int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}
5、纯虚函数和多继承

虚析构:用于解决   在释放基类指针(指向的是子类对象)时,不能调用子类的析构函数的问题。

  • virtual ~类名() {}
  • 解决问题: 通过父类指针指向子类对象释放时候不干净导致的问题
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
 
	//普通析构 是不会调用子类的析构的,所以可能会导致释放不干净
	//利用虚析构来解决
	virtual ~Animal()
	{
		cout << "Animal的析构调用" << endl;
	}
};
 
class Cat:public Animal
{
public:
	Cat(const char * name)
	{
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
	}
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
 
	~Cat()
	{
		cout << "Cat的析构调用" << endl;
		if (this->m_Name != NULL)
		{
			delete[] this->m_Name;
			this->m_Name = NULL;
		}
	}
	char * m_Name;
};
 
 
void test01()
{
	Animal *animal = new Cat("TOM");
	animal->speak();
	delete animal;
}
 
int main()
{
	test01();
	system("pause");
	return 0;
}

纯虚析构:写法  virtual ~类名() = 0

                  类内声明  类外实现

                 如果出现了纯虚析构函数,这个类也算抽象类,不可以实例化对象

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
 
class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
 
	//普通析构 是不会调用子类的析构的,所以可能会导致释放不干净
	//利用虚析构来解决
	/*virtual ~Animal()
	{
		cout << "Animal的析构调用" << endl;
	}*/
 
	//纯虚析构  需要声明 还需要实现  类内声明,类外实现
	virtual ~Animal() = 0;//纯虚析构写法
	//如果函数中出现了 纯虚析构函数,那么这个类也算抽象类
	//抽象类  不可实例化对象
	
};
 
Animal::~Animal()
{
	//纯虚析构函数的实现
	cout << "Animal的纯虚析构调用" << endl;
}
 
// 如果出现了纯虚析构函数,这个类也算抽象类,不可以实例化对象
void func()
{
	//Animal an;  栈       栈和堆都不可以实例化对象
	//Animal *animal = new Animal; 堆
}
 
class Cat:public Animal
{
public:
	Cat(const char * name)
	{
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
	}
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
 
	~Cat()
	{
		cout << "Cat的析构调用" << endl;
		if (this->m_Name != NULL)
		{
			delete[] this->m_Name;
			this->m_Name = NULL;
		}
	}
	char * m_Name;
};
 
 
void test01()
{
	Animal *animal = new Cat("TOM");
	animal->speak();
	delete animal;
}
 
int main()
{
	test01();
	system("pause");
	return 0;
}
 6、向上类型转换和向下类型转换
  • 基类转派生类

            向下类型转换  不安全的

  • 派生类转 基类

            向上类型转换  安全

  • 如果发生多态

           总是安全的

  • 父类中如果写了虚函数,而子类没有任何重写,有意义吗?

          没有意义

 7、多态案例二——PK游戏需求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值