静态联编动态联编
多态性提供接口与具体实现之间的另一层隔离,从而将“what”和“how”分离开来。多态性改善了代码的可读性和组织性,项目不仅在最初创建时期可以扩展,而且当项目在需要有新的功能时也能扩展。
静态多态和动态多态
区别是函数地址的早晚绑定,早绑定(静态联编),晚绑定(动态联编)。
如果函数的调用在编译阶段就可以确定函数的调用地址,并产生地址就是早绑定的。
静态多态:函数重载,运算符重载
动态多态:
先有继承关系
父类中有虚函数,子类重写父类中的虚函数
父类的指针或引用 指向子类的对象
静态多态在编译阶段绑定地址,地址早绑定,静态联编
动态多次在运行阶段绑定地址,地址晚绑定,动态联编
多态原理
当父类写了虚函数后,类内部结构发生改变,多了一个vfptr
vfptr 虚函数表指针 ---- > vftable 虚函数表
虚函数表内部记录着 虚函数的入口地址
当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址
虚函数 关键字 virtual
可以利用指针的偏移调用 函数
((void(*)()) (*(int *)*(int *)animal)) ();
有参的时候,因为调用惯例不同,出栈的时候会崩掉,因为一个认为是主调函数释放,一个认为被调函数释放,可以采用typedef的方式解决。
typedef void( __stdcall *FUNPOINT)(int);
(FUNPOINT (*((int*)*(int*)animal + 1)))(10);
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak() //变成虚函数
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public :
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
//动态多态产生条件:
//先有继承关系
//父类中有虚函数,子类中重写父类中的虚函数
//父类的指针或引用 指向子类的对象
//对于有父子关系的两个类,指针或者引用 是可以直接转换的
void doSpeak(Animal& animal)
{
//如果地址早就绑定好了,地址早绑定,属于静态联编
//如果想调用小猫说话,这个时候函数的地址就不能早就绑定好了,而是在运行阶段再去绑定函数的地址,属于地址晚绑定,也就是动态联编
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
Animal ani;
doSpeak(ani);
}
void test02()
{
cout << "sizeof Animal = " << sizeof(Animal) << endl;
}
int main()
{
test02();
system("pause");
return EXIT_SUCCESS;
}
多态案例 - 计算器案例
设计抽象计算器类,分别实现加减乘计算,继承于抽象计算器类,重写虚函数
利用多态可以调用不同计算器
多态的好处
- 代码可读性强
- 组织结构清晰
- 扩展性强
开闭原则: 对扩展进行开放 对修改进行关闭
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
//无多态情况
//class calculator
//{
//public:
// int getResult(string oper)
// {
// if (oper == "+")
// {
// return m_A + m_B;
// }
// if (oper == "-")
// {
// return m_A - m_B;
// }
// if (oper == "*")
// {
// return m_A * m_B;
// }
// }
// int m_A;
// int m_B;
//};
//设计原则:开闭原则
//对扩展进行开放,对修改进行关闭
//利用多态实现计算器
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int m_A;
int m_B;
};
//加法计算器
class AddCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_A + m_B;
}
};
//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_A - m_B;
}
};
//乘法计算器
class MulCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_A * m_B;
}
};
//除法计算器
void test01()
{
//无多态情况
//calculator c;
//c.m_A = 10;
//c.m_B = 30;
//cout << c.getResult("+") << endl;
//多态,可以改善代码的可读性和可扩展性
AbstractCalculator * calculator = new AddCalculator;
calculator->m_A = 20;
calculator->m_B = 30;
cout << calculator->getResult() << endl;
delete calculator;
calculator = new SubCalculator;
calculator->m_A = 200;
calculator->m_B = 100;
cout << calculator->getResult() << endl;
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
纯虚函数和抽象类
语法: virtual int getResult() = 0;
如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类
虚析构和纯虚析构
虚析构语法:virtual ~Animal(){}
如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数
纯虚析构语法:
virtual ~Animal() = 0;
Animal::~Animal(){ .. }
纯虚析构 需要有声明 也需要有实现
如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
virtual void speak()
{
cout << "动物在说话" << endl;
}
//如果子类中有指向堆区的属性,那么要利用虚析构技术,在delete的时候,调用子类的析构函数
//virtual ~Animal()
//{
// cout << "Animal的析构函数调用" << endl;
//}
//纯虚析构 需要有声明 也需要有实现
//如果一个类中有个纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
virtual ~Animal() = 0;
};
Animal ::~Animal()
{
cout << "Animal 的析构函数调用" << endl;
}
class Cat :public Animal
{
public:
Cat(const char* name)
{
cout << "Cat 的构造函数的调用" << endl;
this->m_Name = new char[strlen(name) + 1];
strcpy(this->m_Name, name);
}
virtual void speak()
{
cout <<this->m_Name<< "小猫在说话" << endl;
}
~Cat()
{
if (this->m_Name)
{
cout << "Cat 析构函数调用" << endl;
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 EXIT_SUCCESS;
}
向上类型转换和向下类型转换
父转子:向下类型转换 不安全
子转父:向上类型转换 安全
如果发生多态,那么转换永远都是安全的
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
virtual void speak()
{
cout << "动物在说话" << endl;
}
//如果子类中有指向堆区的属性,那么要利用虚析构技术,在delete的时候,调用子类的析构函数
//virtual ~Animal()
//{
// cout << "Animal的析构函数调用" << endl;
//}
//纯虚析构 需要有声明 也需要有实现
//如果一个类中有个纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
virtual ~Animal() = 0;
};
Animal ::~Animal()
{
cout << "Animal 的析构函数调用" << endl;
}
class Cat :public Animal
{
public:
Cat(const char* name)
{
cout << "Cat 的构造函数的调用" << endl;
this->m_Name = new char[strlen(name) + 1];
strcpy(this->m_Name, name);
}
virtual void speak()
{
cout <<this->m_Name<< "小猫在说话" << endl;
}
~Cat()
{
if (this->m_Name)
{
cout << "Cat 析构函数调用" << endl;
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 EXIT_SUCCESS;
}
重载、重写、重定义
重载
函数重载
同一个作用域下,函数名称相同,参数个数、顺序、类型不同
重写
子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致称为重写
重定义
子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域
示例
多态案例 - 电脑组装案例
利用多态实现 电脑组装
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//CPU基类
class CPU
{
public:
virtual void calculate() = 0;
};
//显卡基类
class VideoCard
{
public:
virtual void display() = 0;
};
//内存基类
class Memory
{
public:
virtual void storage() = 0;
};
//电脑类
class computer
{
public:
computer(CPU * cpu, VideoCard * vc,Memory * mem)
{
cout << "电脑构造调用" << endl;
this->m_Cpu = cpu;
this->m_Vc = vc;
this->m_Mem = mem;
}
void doWork()
{
this->m_Cpu->calculate();
this->m_Vc->display();
this->m_Mem->storage();
}
~computer()
{
cout << "电脑析构调用" << endl;
if (this->m_Cpu)
{
delete this->m_Cpu;
this->m_Cpu = NULL;
}
if (this->m_Vc)
{
delete this->m_Vc;
this->m_Vc = NULL;
}
if (this->m_Mem)
{
delete this->m_Mem;
this->m_Mem = NULL;
}
}
CPU* m_Cpu;
VideoCard* m_Vc;
Memory* m_Mem;
};
//intel 厂商
class intelCPU :public CPU
{
public:
void calculate()
{
cout << "Intel cpu 计算了" << endl;
}
};
class intelVideoCard :public VideoCard
{
public:
void display()
{
cout << "Intel 显卡显示了" << endl;
}
};
class intelMemory :public Memory
{
public:
void storage()
{
cout << "Intel 内存存储了" << endl;
}
};
//Lenovo厂商
class LenovoCPU :public CPU
{
public:
void calculate()
{
cout << "Lenovo cpu 计算了" << endl;
}
};
class LenovoVideoCard :public VideoCard
{
public:
void display()
{
cout << "Lenovo 显卡显示了" << endl;
}
};
class LenovoMemory :public Memory
{
public:
void storage()
{
cout << "Lenovo内存存储了" << endl;
}
};
void test01()
{
cout << "第一台电脑组成:" << endl;
CPU* intelCpu = new intelCPU;
VideoCard* lenovoVC = new LenovoVideoCard;
Memory* lenovoMem = new LenovoMemory;
computer c1(intelCpu, lenovoVC, lenovoMem);
c1.doWork();
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}