多态性
那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
广义的多态指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
a、 编译时多态性:通过重载函数实现
函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。
b、运行时多态性:通过虚函数实现。
函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
狭义的多态单单指运行时多态。多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。
在继承关系中,虚函数是根据指针指向的对象来确定调用哪个函数,而非虚函数是根据指针的类型来选择调用哪个函数,跟指向的对象无关。
1、构成函数覆盖的条件为:
a)基类函数必须是虚函数(使用virtual关键字进行声明)。
b) 发生覆盖的两个函数要分别位于派生类和基类中。
c) 函数名称与参数列表必须完全相同。
由于C++的多态性是通过虚函数来实现的,所以函数的覆盖总是和多态关联在一起。在函数覆盖的情况下,编译器会在运行时根据对象的实际类型来确定要调用的函数。
2、覆盖和隐藏的区别
a) 派生类的函数与基类的函数完全相同(函数名和参数列表都相同),只是基类的函数没有使用virtual关键字。此时基类的函数将被隐藏,而不是覆盖(请参照上文讲述的函数覆盖进行比较)。
b) 派生类的函数与基类的函数同名,但参数列表不同,在这种情况下,不管基类的函数声明是否有virtual关键字,基类的函数都将被隐藏。注意这种情况与函数重载的区别,重载发生在同一个类中。
3、重载不关心函数返回类型,只关心参数列表,对于同名函数,只要参数列表相同,尽管返回类型不一样,依旧认为是重复定义,会报错。(前提是在同一个类里面)
1.重载:重载从overload翻译过来,是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
示例代码如下:
class A{
public:
void test(int i);
void test(double i);
void test(int i, double j);
void test(double i, int j);
int test(int i); //错误,非重载
};
前四个互为重载函数,最后一个和第一个不是重载函数。
2.隐藏:隐藏是指派生类的函数屏蔽了与其同名的基类函数。注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。
实例代码如下:
#include<iostream>
using namespace std;
class A{
public:
void fun1(int i, int j){
cout << "A::fun1() : " << i << " " << j << endl;
}
};
class B : public A{
public:
//隐藏
void fun1(double i){
cout << "B::fun1() : " << i << endl;
}
};
int main(){
B b;
b.fun1(5); //调用B类中的函数
b.fun1(1, 2); //出错,因为基类函数被隐藏
system("pause");
return 0;
}
3.覆盖:覆盖翻译自override,是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。基类该函数必须被virtual关键字修饰,只有函数体不同(花括号内),派生类调用时会调用派生类的覆盖函数,不会调用被覆盖函数。
实例代码如下:
#include<iostream>
using namespace std;
class A{
public:
virtual void fun3(int i){
cout << "A::fun3() : " << i << endl;
}
};
class B : public A{
public:
//覆盖
virtual void fun3(int i){
cout << "B::fun3() : " << i << endl;
}
};
int main(){
A a;
B b;
A * pa = &a;
pa->fun3(3);
pa = &b;
pa->fun3(5);
system("pause");
return 0;
}
重载和覆盖的区别:
(1)范围区别:覆盖和被覆盖的函数在不同的类中,重载和被重载的函数在同一类中。(2)参数区别:覆盖与被覆盖的函数参数列表一定相同,重载和被重载的函数参数列表一定不同。
(3)virtual的区别:覆盖的基类必须要有virtual修饰,重载函数和被重载函数可以被virtual修饰,也可以没有。
隐藏和覆盖,重载的区别:
(1)与重载范围不同:隐藏函数和被隐藏函数在不同类中。
(2)参数的区别:隐藏函数和被隐藏函数参数列表可以相同,也可以不同,但函数名一定相同;当参数不同时,无论基类中的函数是否被virtual修饰,基类函数都是被隐藏,而不是被覆盖。
#include<iostream>
using namespace std;
class base
{
public:
int a;
void show1();
void show2();
void show3();
virtual void show4();
base(int i);
};
void base::show1()
{
cout << "base show1" << endl;
}
void base::show2()
{
cout << "base show2" << endl;
}
void base::show3()
{
cout << "base show3" << endl;
}
void base::show4()
{
cout << "base show4" << endl;
}
base::base(int i)
{
a = i;
}
class test :public base
{
public:
int b;
test(int i,int j);
void show1(); //重写(覆盖)基类函数
//void show2(); //子类没有定义该函数,继承了基类的该函数
int show3(int j); //虽然和基类的返回值和参数列表不一样,但是只要函数名称相同,便隐藏基类的此函数
void show4(); //重写(覆盖)虚函数,体现多态性
};
void test::show1()
{
cout << "test show1" << endl;
}
/*void test::show2()
{
cout << "test show2" << endl;
}*/
int test::show3(int j)
{
cout << "test show3" << endl;
return 0;
}
void test::show4()
{
cout << "test show4" << endl;
}
test::test(int i, int j) :base(i)
{
b = j;
}
int main()
{
test temp1=test(2,3);
base temp2 = base(4);
base *point;
//子类继承了,基类所有的类成员和类函数
cout << "子类继承了,基类所有的类成员和类函数" << endl;
temp1.show2();
cout << temp1.a << endl<<endl;
//体现了重写特性,参数列表和返回值均和基类一致
cout << "体现了重写特性,参数列表和返回值均和基类一致" << endl;
temp1.show1(); //
cout << endl;
//体现了隐藏特性
//temp1.show3(); //如果直接这样写,会报错“错误 1 error C2660: “test::show3”: 函数不接受 0 个参数”
//这是因为子类的show3隐藏了基类不带参数的show3
//体现了虚函数的作用
cout << "体现了虚函数的作用" << endl;
cout << "虚函数的效果" << endl;
point = &temp1;
point->show4();
point = &temp2;
point->show4();
cout << "非函数的效果" << endl;
point = &temp1;
point->show1();
point = &temp2;
point->show1();
system("pause");
return 0;
}