多态从字面上理解是:一个东西有多种多样的形态。
静态(编译时期)的多态
函数重载
bool compare(int, int){}
bool compare(double, double){}
compare(10, 20); //call compare_int_int 在编译阶段就确定好调用的函数版本
compare(10.5, 20.5); //call compare_douoble_double 在编译阶段就确定好调用的函数版本
模板(函数模板和类模板)
template<typename T>
bool compare(T a, T b){}
compare(10, 20);
=> 编译器发现是int 从原模板里拿int去实例化一个 compare<int>
compare(10.5, 20.5);
=> 编译器发现是double 从原模板里拿double去实例化一个 compare<double>
这些都是在编译时期确定的。
动态(运行时期)的多态
在继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名覆盖方法(虚函数),基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名覆盖方法,称为多态
多态底层是通过动态绑定来实现的,pbase-》指向谁就是访问谁的vfptr
=》继续访问谁的vftable
=》当然调用的是对应的派生类对象的方法了
//动物的基类
class Animal
{
public:
Animal(string name) :_name(name) {}
virtual void bark() {}//叫
protected:
string _name;//动物的名称
};
//以下是动物的实体类
class Cat : public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << _name << " bark: miao miao!" << endl; }
};
class Dog : public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << _name << " bark: wang wang!" << endl; }
};
class Pig : public Animal
{
public:
Pig(string name) :Animal(name) {}
void bark() { cout << _name << " bark: heng heng!" << endl; }
};
1个基类,3个派生类。
现在我们开始定义对象
现在,我们想写这么一个方法:
这样设计,利用函数的重载,太麻烦了。
我们传入的是引用变量来调用虚函数,所以是动态绑定。
但是我们实现的这个API接口不太好,因为它不封闭,我们可以从Animal继承添加更多的新的动物,或者由于现有的变更,我们现有的动物可能要删除掉,不用了。如果新增派生类对象,我们还要再提供一个bark方法,专门接收新添加的对象的引用。如果因为需求的更改,把现有的动物的实体类删除了,还要删除相对应的bark方法。
上面的设计无法做到我们软件涉及要求的“开-闭“原则
软件设计有六大原则 其中一个就是: “开-闭“原则 对修改关闭,对扩展开放
用基类指针(引用),可以接收不同的派生类对象
void bark(Animal *p)
{
p->bark();//编译时看到Animal::bark是虚函数,就进行动态绑定
/*
p->cat Cat vftable &Cat::bark
p->dog Dog vftable &Dog::bark
p->pig Pig vftable &Pig::bark
*/
}
int main()
{
Cat cat("猫咪");
Dog dog("二哈");
Pig pig("佩奇");
bark(&cat);
bark(&dog);
bark(&pig);
return 0;
}
不管传哪个派生类对象进去,代码都是一样的!
编译时看到Animal::bark是虚函数,就进行动态绑定
访问指针指向的前4个字节:即对象的vfptr虚函数指针,然后访问指向的对象的虚函数表了,然后取相应的虚函数。
这样就 高内聚,低耦合了
继承的好处是什么?
1.可以做代码的复用
2.在基类中给所有派生类提供统一的虚函数接口,让派生类进行重写,然后就可以使用多态了
抽象类
抽象类和普通类有什么区别?
抽象类一般不是用来抽象某一个实体类型,抽象类不能实例化对象,但是可以定义指针或者引用变量。普通类定义指针或者引用变量或者实例化对象都可以。
一般把什么类设计成抽象类? 基类
动物的基类本身就算是泛指,
类->抽象一个实体的类型
//动物的基类
class Animal
{
public:
Animal(string name) :_name(name) {}
virtual void bark() {}//叫
protected:
string _name;//动物的名称
};
//以下是动物的实体类
class Cat : public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << _name << " bark: miao miao!" << endl; }
};
class Dog : public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << _name << " bark: wang wang!" << endl; }
};
class Pig : public Animal
{
public:
Pig(string name) :Animal(name) {}
void bark() { cout << _name << " bark: heng heng!" << endl; }
};
定义Animal的初衷,并不是让Animal抽象某个实体的类型
定义基类的好处:
1.string _name; 让所有的动物实体类通过继承Animal直接复用该属性
2.给所有的派生类保留统一的覆盖/重写接口
我们定义了一个Animal,我们不知道是什么动物,当然也不知道它是怎么叫的。它里面的方法是给所有派生类保留的统一的重写接口。
我们不知道Animal的方法该怎么写,所以我们直接让这个基类的方法等于0
这个函数就叫做纯虚函数了。
拥有纯虚函数的类,叫做抽象类!(Animal此时就是抽象类了)
Animal a; 是不可以的!!!
抽象类不能再实例化对象了,但是可以定义指针和引用变量
class Animal
{
public:
Animal(string name) :_name(name) {}
//纯虚函数
virtual void bark() = 0;
protected:
string _name;
};
//以下是动物实体类
class Cat : public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << _name << " bark: miao miao!" << endl; }
};
class Dog : public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << _name << " bark: wang wang!" << endl; }
};
class Pig : public Animal
{
public:
Pig(string name) :Animal(name) {}
void bark() { cout << _name << " bark: heng heng!" << endl; }
};
void bark(Animal *p)
{
p->bark();//Animal::bark虚函数,动态绑定了
}
抽象类的第2个例子
//汽车的基类
class Car//抽象类
{
public:
Car(string name, double oil) :_name(name), _oil(oil) {}
//获取汽车剩余油量还能跑的公里数
double getLeftMiles()//这里是静态绑定
{
//1L油可以跑多少公里*剩余的油量 10 * oil
return _oil * this->getMilesPerGallon();//在这里发生动态绑定了
}
string getName()const { return _name; }
protected:
string _name;
double _oil;
virtual double getMilesPerGallon() = 0;//纯虚函数,保护,派生类可以访问,外部不能访问
//不同汽车 1L油可以跑多少公里 的数是不一样的
};
class Bnze : public Car
{
public:
Bnze(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 20.0; }
};
class Audi : public Car
{
public:
Audi(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 18.0; }
};
class BMW : public Car
{
public:
BMW(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 19.0; }
};
//给外部提供一个同一的获取汽车剩余路程数的API
void showCarLeftMiles(Car &car)
{
cout<<car.getName() << " left miles:"
<< car.getLeftMiles() << "公里" <<endl;
//静态绑定 call Car::getLeftMiles()
}
int main()
{
Bnze b1("奔驰", 20.0);
Audi a("奥迪", 20.0);
BMW b2("宝马", 20.0);
showCarLeftMiles(b1);
showCarLeftMiles(a);
showCarLeftMiles(b2);
return 0;
}