多态
多态:一个接口,多种实现。
静态多态:编译时 (函数重载,操作符重载和模板)
动态多态:运行时(派生类,虚函数)—类继承
重载:是函数或者方法有相同的名称,但是参数列表不相同,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
重写(覆盖):是子类对父类的允许访问的方式的实现过程进行重新编写,返回值和形参不能改变(virtual函数)。
重定义(隐藏):重新定义父类的同名成员函数(非virtual函数)。
重定义
#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout<< " A::print()"<<endl;
}
};
class B:public A
{
public:
void print()
{
cout<< " B::print()"<<endl;
}
};
int main()
{
A a;
B b;
a.print();
b.print();
A & obj=b; //隐式类型转转
obj.print(); //这里输出 “A::print()”
return 0;
}
不是多态看类型,因此调用的是父类的print()函数。
重写
#include<iostream>
using namespace std;
class A
{
public:
virtual void print()
{
cout<< " A::print()"<<endl;
}
};
class B:public A
{
public:
void print()
{
cout<< " B::print()"<<endl;
}
};
int main()
{
A a;
B b;
a.print();
b.print();
A & obj=b; //隐式类型转换 这里进行转换时 虚函数表的信息不会发生变化
obj.print(); //这里输出 B::print()
return 0;
//另一种 方法 父类的指针指向子类的对象
//A * pa = new B(); //子类可以转化为父类
//pa->print(); // 这里输出 B::print()
//子类不允许访问父类
//B* pb = new A(); 错误的 父类不允许转化为子类
}
动态多态的作用
#include<iostream>
#include<vector>
using namespace std;
class A
{
public:
virtual void print()
{
cout<< " A::print()"<<endl;
}
};
class B:public A
{
public:
void print()
{
cout<< " B::print()"<<endl;
}
};
class C:public A
{
public:
void print()
{
cout<< " C::print()"<<endl;
}
};
void print(vector<A*>& arr)
{
for(auto it :arr)
{
it->print();
}
}
int main()
{
// 动态多态的作用
A* pa1 = new B(); //可以 子类可转化为父类
pa1->print();
A* pa2 = new C(); //子类可转化为父类
pa2->print();
vector<A*> arr;
arr.push_back( new B());
arr.push_back(new C());
print(arr);
return 0;
}
virtual关键字不能修饰的函数有哪些?
1、构造函数
2、全局函数
3、静态成员函数
4、友元函数
5、inline函数 在编译时进行展开
纯虚函数表:
纯虚函数的定义 virtual void test()=0;
带有纯虚函数的类可以称为纯虚类
虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象
#include<iostream>
#include<vector>
using namespace std;
class Car
{
public:
~Car()
{
cout<<"Car destructor" <<endl;
}
};
class BWM: public Car
{
private:
char * _name;
public:
BWM(const char *s):_name(nullptr)
{
int len = strlen(s);
_name = new char[len +1];
strncpy(_name,s,len);
_name[len] = '\0';
}
~BWM()
{
cout<< "BWM destructor" <<endl;
delete[] _name;
}
};
int main()
{
Car *p = new BWM("bwm770");
delete p; //输出的是"Car destructor"。 没有输出 " BWM destructor"会造成内存泄漏
return 0;
}
如何解决,使用虚析构函数。
#include<iostream>
#include<cstdio>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
class Car
{
public:
virtual ~Car() //声明成虚析构
{
cout<<"Car destructor" <<endl;
}
};
class BWM: public Car
{
private:
char * _name;
public:
BWM(const char *s):_name(nullptr)
{
int len = strlen(s);
_name = new char[len +1];
strncpy(_name,s,len);
_name[len] = '\0';
}
~BWM()
{
cout<< "BWM destructor" <<endl;
delete[] _name;
}
};
int main()
{
Car *p = new BWM("bwm770");
delete p;
return 0;
}
为什么会出现此问题?
在没有对基类的析构函数加虚函数时
可以正常释放父类“car destructor”的资源,但是不能释放子类“BWM destructor”的资源。
通常情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。**原因是指针p是Car(父类)类型的指针,释放时只进行Car类的析构函数,**对其加上Virtual后会释放父类和子类的内存 。
抽象基类与虚函数
在设计时,常常希望基类仅仅作为其派生类的一个接口。这就是说,仅想对基类进行向上类型转换,使用它的接口,而不希望用户实际的创建一个基类的对象。同时创建一个纯虚函数允许接口中放置成员原函数,而不一定要提供一段可能对这个函数毫无意义的代码。
做到这点,可以在基类中加入至少一个纯虚函数(pure virtual function),使得基类称为抽象类(abstract class).
1、纯虚函数使用关键字virtual,并在其后面加上=0。如果试图去实例化一个抽象类,编译器则会阻止这种操作。
2、当继承一个抽象类的时候,必须实现所有的纯虚函数,否则由抽象类派生的类也是一个抽象类。
3、Virtual void fun() = 0;告诉编译器在vtable中为函数保留一个位置,但在这个特定位置不放地址。
#include<iostream>
using namespace std;
//抽象制作饮品
class AbstractDrinking{
public:
//烧水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加入辅料
virtual void PutSomething() = 0;
//规定流程
void MakeDrink(){
Boil();
Brew();
PourInCup();
PutSomething();
}
};
//制作咖啡
class Coffee : public AbstractDrinking{
public:
//烧水
void Boil(){
cout << "煮农夫山泉!" << endl;
}
//冲泡
void Brew(){
cout << "冲泡咖啡!" << endl;
}
//倒入杯中
void PourInCup(){
cout << "将咖啡倒入杯中!" << endl;
}
//加入辅料
void PutSomething(){
cout << "加入牛奶!" << endl;
}
};
//制作茶水
class Tea : public AbstractDrinking{
public:
//烧水
void Boil(){
cout << "煮自来水!" << endl;
}
//冲泡
void Brew(){
cout << "冲泡茶叶!" << endl;
}
//倒入杯中
void PourInCup(){
cout << "将茶水倒入杯中!" << endl;
}
//加入辅料
void PutSomething(){
cout << "加入食盐!" << endl;
}
};
//业务函数
void DoBussiness(AbstractDrinking* drink){
drink->MakeDrink();
delete drink;
}
void test(){
DoBussiness(new Coffee);
cout << "--------------" << endl;
DoBussiness(new Tea);
}
int main()
{
test();
return 0;
}