由于之前一直在用C编写代码,最近工作中需要用到C++编写代码,之前对C++中重写、重载、隐藏这3个概念不甚了解,为了整明白这三个概念的定义和作用的,花了半天时间在网上搜索了一些资料,看了一下关于C++方面的书(《C++ Primer plus》,《高质量C++编程指南》),算是对这三个概念有了一定的了解。
1、重载
重载就是简单的复用一个已经存在的名字,来操作不用的类型。这个名字可以是一个函数名,也可以是一个操作符。由于主要是针对函数的重载,所以对于操作符的重载在后续进行解释。
虽然可以通过默认参数的方式可以使用不同数据的参数可以调用同一个函数,但是对于不同参数类型的操作,可就是爱莫能助了。为了实现同一个函数来实现不同类型的操作,这就需要C++中的一个重要的特性:重载。
实现函数重载的主要条件是:
1)首先发生重载的函数需要在相同的作用域中;
2)函数名称需要相同;
3)函数的参数类型不相同;
4)与virtual关键字无关;
下面的示例中,Base类中的 getIndex(int x)和getIndex(float x)为相互重载,与virtual无关。当调用getIndex函数的时候根据传入的参数选择不同的函数进行执行。
#include <iostream>
#include <stdio.h>
using namespace std;
class Base
{
public:
virtual void getIndex(int x)
{
cout<<"Base x="<<x<<endl;
}
virtual void getIndex(float x)
{
cout<<"Base x="<<x<<endl;
}
};
class Derived:public Base
{
public:
virtual void Derived(string x)
{
cout<<"Derived x="<<x<<endl;
}
};
int main()
{
Base base_obj;
base_obj.getIndex(2.14f);//Base float x=2.14
base_obj.getIndex(214); //Base int x=214
return 0;
}
由于getIndex(int x)和getIndex(float x)为相互重载。其中base_obj.getIndex(2.14f)函数由于参数为float类型,因此调用getIndex(float x)输出Base float x=2.14,而base_obj.getIndex(214)函数参数为int类型,因此调用getIndex(int x),输出Base int x=214。
PS:对第一个条件进行说明,为什么只有在相同的作用域中才能形成重载?虽然Base类和 Derived类在同一个文件中,但是在各自类的作用范围中定义了处理不同类型的getIndex函数,只有Base类中的两个函数互为重载。而Derived中定义的getIndex,虽然函数参数为string,但由于在不同的作用范围中因此不能和Base类中的函数形成重载。
2、重写
有时候希望同一个方法在基类和派生类中表现不同的行为。也就是说通过不同的对象调用,来实现不同的功能。这就是面向对象中的多态,同一个方法在不同的上下文中表现出多种形态。重写的时候就引入了virtual,将需要在派生类中重写的函数在基类中声明为virtual类型的。
实现重写的特性
1)在基类中将需要重写的函数声明为virtual;
2)派生类类和基类中的函数的名称相同;
3)函数的参数类型相同;
4)在不同的作用范围中;(基类和派生类中)
#include <iostream>
#include <stdio.h>
using namespace std;
class Base
{
public:
virtual void getIndex(int x)
{
cout<<"Base int x="<<x<<endl;
}
virtual void getIndex(float x)
{
cout<<"Base float x="<<x<<endl;
}
};
class Derived:public Base
{
public:
void getIndex(float x)
{
cout<<"Derived x="<<x<<endl;
}
};
int main()
{
Derived derived_obj;
Derived *pd;
Base *pb;
pd = &derived_obj;
pb = &derived_obj;
pd->getIndex(2.14f);//Derived x=2.14
pb->getIndex(2.14f);//Derived x=2.14
return 0;
}
如上述代码,其中Derived类继承Base类,由于在Base类中的getIndex(float x)函数声明为virtual,并且基类和派生类中的函数名称、参数类型都相同,因此在派生类Derived中重写了父类中的函数。所以无论是通过指向基类还是派生类的指针调用,最终还是调用的派生类中定义的getIndex(float x),输出“Derived x=2.14”。
PS:如果需要在派生类中重现实现基类中的方法,通常将基类中的该方法声明为virtual,这样当最终访问的时候无论是通过指向基类的还是指向派生类的指针来调用该方法是,最终会根据对象类型来访问。如上述代码中pd,pb分别为指向派生类和基类的指针,但最终来选择getIndex函数的版本时,依据对象derived_obj的类型来选择方法的版本。
3)隐藏
隐藏通常是指函数或者类被同名的函数或者方法隐藏了。派生类中的函数屏蔽了基类的同名函数。
隐藏的条件:
1)基类和派生类中函数名称相同,但是参数的类型不同,无论基类中函数是否声明为virtual,派生类中的函数将屏蔽基类中定义的方法;
2)基类和派生类中函数名称相同,参数类型也相同,如果基类中函数未声明为vitrual,则派生类中的函数将屏蔽基类中定义的方法;
#include <iostream>
#include <stdio.h>
using namespace std;
class Base
{
public:
virtual void getIndex(int x)
{
cout<<"Base getindex x="<<x<<endl;
}
void printer(int x)
{
cout<<"Base print x="<<x<<endl;
}
};
class Derived:public Base
{
public:
virtual void getIndex(float x)
{
cout<<"Derived float x="<<x<<endl;
}
void printer(int x)
{
cout<<"Derived print x="<<x<<endl;
}
};
int main()
{
Derived derived_obj;
Derived *pd;
Base *pb;
pd = &derived_obj;
pb = &derived_obj;
pb->getIndex(2.14f);//Base getindex x=2
pd->getIndex(2.14f);//Derived float x=2.14
pb->printer(214);//Base print x=214
pd->printer(214);//Derived print x=214
return 0;
}
如上述代码中,在基类和派生类中getIndex函数虽然函数的名称相同和参数类型不同,但是由于在不同的作用范围中不能构成重载,因此Derived::getIndex屏蔽了Base::getIndex方法。由于参数类型不同,因此隐藏与基类中是否声明为virtual无关。
在基类和派生类中printer函数虽然函数的名称和函数的参数均相同,似乎和可以满足重写的条件,但是由于在基类中没有声明为virtual因此不能构成重写,所以Derived::getIndex屏蔽了Base::getIndex方法。
参考:
1、 http://blog.csdn.net/pengzhixi/article/details/4249404
2、《C++ Primer plus》
3、《
高质量C++编程指南》