这里插播一则对于多态的认识,多态是面相对相三巨头之一(封装,继承,多态),而且还是其中最神秘的一个。对于刚刚步入编程大门的人,他的很多故事都鲜为人知。想要想要了解他为何能成为三巨头之一,就要先看看他的一众小弟,重载,重写,隐藏,虚函数,接下来让我们好好了解一下他们。
1.重载(最老实的小弟)
(1)范围:在同一作用域中,也就是在同一个类中或同一源文件中;
(2)函数名:函数名必须相同;
(3)参数:参数类型,参数个数,参数顺序中至少有一项不同;
(4)返回值类型:不影响,可以相同,可以不同。
(5)关键字修饰:被const或volatile关键字修饰不同的也会重载,这是一种常被人遗忘的重载方式。
(6)virtual关键字可有可无。
class A
{
void func(int i) {......} //函数
void func(char c) {......} //函数的重载
void func(int i, char c) {......} //函数的重载
void func(char c, int i) {......} //函数的重载
void func(int i) const {......} //函数的重载
};
之所以说他是多态手下最老实的小弟了,是因为他从不出门,只在家里晃悠,也就是只有在同一作用域里的函数会发生重载。再者重载始终如一,不像他的其他朋友那么机灵善变。当程序编译时编译器会以作用域+返回类型+函数名+参数列表的形式将其重新命名。之后这个名字就再也没法改变了,重载的使命也就完成了。当我们调用函数时写入不同类型的参数或关键字时,程序会自动调用不同的函数。
#include<iostream>
using namespace std;
class A
{
public:
void func(int i)
{
cout<<i<<endl;
}
void func(char c)
{
cout<<c<<endl;
}
void func(int i, char c)
{
cout<<i<<" "<<c<<endl;
}
void func(char c, int i)
{
cout<<c<<" "<<i<<endl;
}
void func(int i) const
{
cout<<i<<" "<<i<<endl;
}
};
int main()
{
A a;
const A aa;
a.func(1);
a.func('A');
a.func(1,'A');
a.func('A',1);
aa.func(1);
return 0;
}
输出结果:
1
A
1 A
A 1
1 1
2.覆盖(又名重写,好动,虚函数的好兄弟):
(1)范围:不同作用域,只在继承中,分别位于基类和派生类中;
(2)要求:基类中函数必须加virtual;
(3)函数:派生类中的函数必须要与基类中的完全相同(函数名,返回值类型,参数列表)。
(4)重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的。
class A
{
public:
virtual void func()
{
cout<<"A"<<endl;
}
};
class B:public A
{
public:
void func()
{
cout<<"B"<<endl;
}
};
只要基类指针(要用指针调用才能体现)指向哪个类,就会调用哪个类中的函数,所以它很不消停。这也正是他多态的体现。重写是一定要基于虚函数才能实现的,所以虚函数和重写是一对好兄弟,形影不离。
#include <iostream>
using namespace std;
class A
{
public:
virtual void func()
{
cout<<"A"<<endl;
}
virtual ~A(){};
};
class B:public A
{
public:
void func()
{
cout<<"B"<<endl;
}
~B(){};
};
int main()
{
A *a=new A;
a->func();
delete a;
a=new B;
a->func();
delete a;
return 0;
}
输出结果:
A
B
3.重定义(又名隐藏)
(1)范围:不同作用域中,基类与派生类中;
(2)函数名:函数名必须相同;
(3)返回值可以不同;
(4)参数不同时,不论有无 virtual 关键字,都为隐藏。
(5)参数相同,但是基类函数没有 virtual关键字这也是隐藏。
#include<iostream>
using namespace std;
class A
{
public:
virtual int func1()
{
cout<<"A::func1"<<endl;
}
int func2(float y)
{
cout<<"A::func2"<<","<<y<<endl;
}
virtual ~A(){};
};
class B:public A
{
public:
int func1()
{
cout<<"B::func1"<<endl;
}
int func2(int x,float y)
{
cout<<"B::func2"<<","<<y<<endl;
}
~B(){};
};
int main()
{
int i=1;
float f=1.1;
A a;
B b;
cout<<"-----------pa=a"<<endl;
A *pa=&a;
pa->func1();
pa->func2(f);
cout<<"-----------pa=b"<<endl;
pa=&b;
pa->func1();
pa->func2(f);
cout<<"-----------pb=a"<<endl;
B *pb=(B *)&a;//强制类型转化
pb->func1();
pb->func2(i,f);
cout<<"-----------pb=b"<<endl;
pb=&b;
pb->func1();
pb->func2(i,f);
return 0;
}
输出结果:
-----------pa=a
A::func1
A::func2,1.1
-----------pa=b
B::func1
A::func2,1.1
-----------pb=a
A::func1
B::func2,1.1
-----------pb=b
B::func1
B::func2,1.1
可以看出如果是基类指针(有一般都是),不论指向谁,子类中同名函数会被隐藏,将会调用基类的函数。相反,如果是子类指针,不论指向谁,基类中同名函数会被隐藏,将会调用子类的函数。
总结:
关于多态网上有着各种各样的说辞,看着让人眼花缭乱,难辨是非。经过我的分析和试验,最终得出了以上结论,如果有什么不对的地方还望指出。