多态(polymorphism)的概念:通俗来说,就是多种形态。比如动物会叫,鸡叫起来是“咯咯咯”,鸭叫起来是“嘎嘎嘎”,这就是一种多态,即对同一行为的不同表现
多态分为编译时多态(静态多态)和运⾏时多态(动态多态),编译时多态(静态多态)主要就是我们前⾯讲的函数重载和函数模板,针对同一对象对同一行为的不同处理带来的不同表现,这⾥我们重点讲运⾏时多态,运⾏时多态,具体点就是去完成某个⾏为(函数),可以传不同的对象就会完成不同的⾏为,就达到多种形态,针对的是不同的对象。
1.多态的定义及实现
多态是⼀个继承关系的下的类对象,去调⽤同⼀函数,产⽣了不同的⾏为。注意:必须是引用或指针去调用虚函数才能实现多态,那么什么是虚函数呢?类成员函数前⾯加virtual修饰,那么这个成员函数被称为虚函数。注意⾮成员函数不能加virtual修饰。子类要想实现多态需对父类的虚函数进行重写,派⽣类中有⼀个跟基类完全相同的虚函数(即派⽣类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派⽣类的虚函数重写了基类的虚函数。(注:其实派生类的虚函数不加virtual也可以构成重载,但为了规范,建议加上)
以下是多态的一个简单实现:
class Animal
{
public:
void eat(string food)
{
cout << "我吃了:" << food << endl;
}
void virtual howl()
{
cout << "我啥动物都不知道怎么叫?" << endl;
}
};
class Dog :public Animal
{
public:
void virtual howl()
{
cout << "汪汪汪,咬洗你!" << endl;
}
};
class Cat :public Animal
{
public:
void virtual howl()
{
cout << "喵~" << endl;
}
};
class Human :public Animal
{
public:
void virtual howl()
{
cout << "不是,哥们?" << endl;
}
};
void AnimalHowal(Animal& a)
{
a.howl();
}
int main()
{
Animal a1;
Dog d1;
Cat c1;
Human h1;
AnimalHowal(a1);
AnimalHowal(d1);
AnimalHowal(c1);
AnimalHowal(h1);
return 0;
}
2.关于多态的默认参数问题
请看以下程序:
class Animal
{
public:
void virtual eat(string food = "鱼")
{
cout << "我吃了:" << food << endl;
}
void virtual howl()
{
cout << "我啥动物都不知道怎么叫?" << endl;
}
};
class Dog :public Animal
{
public:
void virtual howl()
{
cout << "汪汪汪,咬洗你!" << endl;
}
void virtual eat(string food = "狗粮")
{
cout << "我吃了:" << food << endl;
}
};
int main()
{
Animal a;
a.eat();
Dog d;
Animal& d1 = d;
d1.eat();
return 0;
}
请问以上程序的运行结果是什么?
相信很多同学都会直接回答:我吃了:鱼
我吃了:狗粮
而事实情况可能会让你大跌眼镜:
这里涉及到一个隐含知识点:派生类在重写虚函数时实际上是重写的它的函数体,参数列表仍沿用的父类,因此默认参数仍使用的是父类的默认参数
3.析构函数的重写
编译器编译后各类中析构函数的名称统⼀处理成destructor,所以基类的析构函数加了vialtual修饰,派⽣类的析构函数就构成重写。
class Animal
{
public:
void virtual eat(string food = "鱼")
{
cout << "我吃了:" << food << endl;
}
void virtual howl()
{
cout << "我啥动物都不知道怎么叫?" << endl;
}
virtual ~Animal()
{
cout << "调用了Animal的析构" << endl;
}
};
class Dog :public Animal
{
public:
void virtual howl()
{
cout << "汪汪汪,咬洗你!" << endl;
}
void virtual eat(string food = "狗粮")
{
cout << "我吃了:" << food << endl;
}
~Dog()
{
cout << "调用了Dog的析构" << endl;
delete[] friends;
}
string* friends = new string[10];
};
int main()
{
Animal* a = new Animal;
Animal* d = new Dog;
delete a;
delete d;
return 0;
}
这里Dog中存了需要手动释放的资源friends,如果未对Animal的析构进行重写的话,delete d任然调用的是Animal的析构,friends未被释放,会造成内存泄露
4.override 和 final关键字
C++11提供了override,可以帮助⽤⼾检测是否重写。如果我们不想让派⽣类重写这个虚函数,那么可以⽤final去修饰。
class Animal
{
public:
void virtual eat(string food = "鱼")
{
cout << "我吃了:" << food << endl;
}
void virtual howl()
{
cout << "我啥动物都不知道怎么叫?" << endl;
}
virtual ~Animal()
{
cout << "调用了Animal的析构" << endl;
}
};
class Dog :public Animal
{
public:
void virtual howl()
{
cout << "汪汪汪,咬洗你!" << endl;
}
void virtual eat(string food = "狗粮") override
{
cout << "我吃了:" << food << endl;
}
~Dog()
{
cout << "调用了Dog的析构" << endl;
delete[] friends;
}
string* friends = new string[10];
};
class Animal
{
public:
void virtual eat(string food = "鱼")
{
cout << "我吃了:" << food << endl;
}
void virtual howl() final
{
cout << "我啥动物都不知道怎么叫?" << endl;
}
virtual ~Animal()
{
cout << "调用了Animal的析构" << endl;
}
};
class Dog :public Animal
{
public:
void virtual howl()
{
cout << "汪汪汪,咬洗你!" << endl;
}
void virtual eat(string food = "狗粮")
{
cout << "我吃了:" << food << endl;
}
~Dog()
{
cout << "调用了Dog的析构" << endl;
delete[] friends;
}
string* friends = new string[10];
};
5.纯虚函数和抽象类
在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象,如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。抽象类就好像你们的老板,不完成他给定的任务(实现虚函数),你就别想下班qwq
class Boss
{
public:
void virtual task() = 0;
};
class staff:Boss
{
public:
string id;
};
int main()
{
Boss b;
staff s;
return 0;
}
class Boss
{
public:
void virtual task() = 0;
};
class staff:Boss
{
public:
void virtual task()
{
cout << "我想摸鱼~" << endl;
}
string id;
};
int main()
{
staff s;
s.task();
return 0;
}
那么最后,祝大家国庆节快乐,byebye~(诶不是我国庆假怎么只有一天了啊qwq)