一、重载(overload)
指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
(1)相同的范围(在同一个作用域中即同一类中) ;
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
(5)返回值可以不同;
特殊情况:若某一个重载版本的函数前面有virtual修饰,则表示它是虚函数。但它也是属于重载的一个版本
不同的构造函数(无参构造、有参构造、拷贝构造)是重载的应用
作用效果和原理:编译器根据函数不同的参数表,将函数体与函数调用进行早绑定,是静态的。
重载与多态无关,只是一种语言特性,与面向对象无关。重载不关心函数的返回值类型。
二、重写(也称为覆盖 override)
是指派生类重新定义基类的虚函数,即会覆盖基类的虚函数(多态性)特征是:
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)函数名字相同;
(3)参数列表相同;
(4)基类函数必须有 virtual 关键字,不能有 static 。
(5)返回值相同(或是协变即基类中返回类型是基类引用或指针则派生类中返回可以修改为派生类引用或指针),否则报错;
(6)重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的
特殊情况:若派生类重写的虚函数属于一个重载版本,则该重写的函数会隐藏基类中与虚函数同名的其他函数,另外的版本将被隐藏,派生类无法使用。
作用效果:父类的指针或引用根据传递给它的子类地址或引用,动态地调用属于子类的该函数。这个晚绑定过程只对virtual函数起作用
注意:
1)如果是通过引用或者指针而不是对象调用(如基类类型的指针指向派生类),调用函数的处理方法(是否多态性)取决于是否重写了虚函数。如果是,则根据多态性,调用指向或引用的对象的类(如派生类的函数)。如果不是,则是正常的静态函数调用,调用引用类型或者指针类型的类(如基类函数)。
2)派生类中的所有不属于基类的信息都无法通过基类指针(或基类引用)或派生类对象初始化基类对象(会产生对象切片)来获取,因为编译器在解析该指针或引用指向的内存区时是按照基类的信息来解释的。除了基类指针或基类引用间接指向派生类类型且基类中使用了virtual时,编译器则会通过基类信息动态联编派生类中的重写的函数,产生多态。
三、重定义(也称为隐藏redefine)
派生类对基类的成员函数重新定义,即派生类定义了某个函数,该函数的名字与基类中的函数名字一样。 派生类对象中的(非派生类指针或引用)基类的函数将被隐藏
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)函数名字相同;
(3)返回值可以不同;
(4)参数不同。此时,不论有无 virtual 关键字。
(5)参数相同,但是基类函数没有 virtual关键字。
特殊情况:若派生类定义的该函数与基类的成员函数完全一样(返回值、形参列表均相同),且基类的该函数为virtual,则属于派生类重写基类的虚函数。
作用效果:若重新定义了基类中的一个重载函数,则在派生类中,基类中该名字的函数(包括所有重载版本)都被自动隐藏,包括同名的虚函数。
注意:是否是否隐藏决定于指针的类型,与指针指向什么无关。派生类重定义的基类函数只有基类类型指针才能访问它,派生类类型指针只能访问重定义的那个函数,虽然基类函数仍然在派生类代码中。
实例1:
2 #include <complex>
3 using namespace std;
4
5 class Base
6 {
7 public:
8 virtual void a(int x) { cout << "Base::a(int)" << endl; }
9 // overload the Base::a(int) function
10 virtual void a(double x) { cout << "Base::a(double)" << endl; }
11 virtual void b(int x) { cout << "Base::b(int)" << endl; }
12 void c(int x) { cout << "Base::c(int)" << endl; }
13 };
14
15 class Derived : public Base
16 {
17 public:
18 // redefine the Base::a() function
19 void a(complex<double> x) { cout << "Derived::a(complex)" << endl; }
20 // override the Base::b(int) function
21 void b(int x) { cout << "Derived::b(int)" << endl; }
22 // redefine the Base::c() function
23 void c(int x) { cout << "Derived::c(int)" << endl; }
24 };
25
26 int main()
27 {
28 Base b;
29 Derived d;
30 Base* pb = new Derived;
31 // ----------------------------------- //
32 b.a(1.0); // Base::a(double)
33 d.a(1.0); // Derived::a(complex)
34 pb->a(1.0); // Base::a(double), This is redefine the Base::a() function
35 // pb->a(complex<double>(1.0, 2.0)); // clear the annotation and have a try
36 // ----------------------------------- //
37 b.b(10); // Base::b(int)
38 d.b(10); // Derived::b(int)
39 pb->b(10); // Derived::b(int), This is the virtual function
40 // ----------------------------------- //
41 delete pb;
42
43 return 0;
44 }
1.Base类中的第二个函数a是对第一个的重载
2.Derived类中的函数b是对Base类中函数b的重写,即使用了虚函数特性。
3.Derived类中的函数a是对Base泪中函数a的隐藏,即重定义了。
4. pb指针是一个指向Base类型的指针,但是它实际指向了一个Derived的空间,这里对pd调用函数的处理(多态性)取决于是否重写(虚函数特性)了函数,若没有,则依然调用基类。
5.只有在通过基类指针或基类引用 间接指向派生类类型时多态性才会起作用。
6.因为Base类的函数c没有定义为virtual虚函数,所以Derived类的函数c是对Base::c()的重定义
实例2:
基类和派生类的结构
//Base class class Test { public: int a; Test() { cout<<"Test() 无参构造函数!"<<endl; } Test(int data) { a = data; cout<<"Test(int data) 有参构造函数!"<<endl; } Test(const Test &tmp) { a = tmp.a; cout<<"Test 拷贝构造函数!!"<<endl; } //基类中对函数名f,进行了重载。其中最后一个重载函数为虚函数 void f()const { cout<<"调用 void Test::f()"<<endl; } //overload int f(int data) const { cout<<"调用 Test f(int data)"<<endl; return 1; } //overload 虚函数 virtual double f(int dataA,int dataB) { cout<<"调用 Test f(int a,int b)"<<endl; return dataA*dataB/2.0; } }; class XX: public Test { public: Test atest;//先调用基类的构造函数,然后对象成员的构造函数,最后才是派生类的构造函数 XX() { cout<<"XX() 无参构造函数被调用!"<<endl; } //对基类的函数名f,进行了重定义。则会隐藏基类中的其他f函数 //redefine int f() const { cout<<" 调用 XX f()函数"<<endl; return 1; } //重写基类的虚函数 //redefine override double f(int dataA,int dataB) { cout<<"调用 XX f(int dataA,int dataB)函数"<<endl; return (dataA+dataB)/2.0; } };
int main() { //-----test 1------------------------ cout<<"-------test 1------------"<<endl; //Base class Test aaTest; aaTest.f(); aaTest.f(12); aaTest.f(10,20); //derived class XX d; d.f(); // d.f(2); //error C2661: 'f' : no overloaded function takes 1 parameters d.f(10,20); //--------test 2---------------------------------- cout<<"-------test 2------------"<<endl; Test b = d; b.f(); b.f(10,20);//调用的是基类的函数,不发生多态 //--------test 3---------------------------------------- cout<<"-------test 3------------"<<endl; Test &bR = d;//引用 b.f();//f()不是虚函数,调用基类的函数 bR.f(10,20);//调用的是派生类的函数,发生多态 //--------test 4-------------------------------------- cout<<"-------test 4------------"<<endl; Test* pB = &d; b.f(); pB->f(10,20);//调用的是派生类的函数,发生多态 return 1; }
2)对象切片原理
对象切片产生的原因是bitwise(逐位)的copy构造函数的调用。在“MyBase