重写、重载与重定义这三个相关概念比较容易弄混,这里集中整理一下。
- 重载overload 函数名相同,参数列表不同,重载发生在类的内部进行,virtual关键字可有可无。
- 重写override 也叫做覆盖,派生类函数覆盖基类函数。子类重新定义父类中有相同名称和参数的虚函数。
- 重定义redefining 也叫做隐藏,派生类的函数屏蔽了与其同名的基类函数。子类重新定义父类中有相同名称(参数不同)的非虚函数或者虚函数。子类重新定义父类中有相同名称(参数相同)的非虚函数。
在重定义中,子类重新定义父类中有相同名称(参数相同)的虚函数,这叫做覆盖。
相关概念整清楚了下面来看代码:
- 基类相关的代码
1 class Base 2 { 3 private: 4 void say() { cout << "Base say()" << endl; } 5 public: 6 virtual void display() { cout << "Base display()" << endl; } 7 8 void exec() 9 { 10 display(); 11 say(); 12 } 13 14 void f1(string a) { cout << "Base f1(string)" << endl; } 15 void f1(int a) { cout << "Base f1(int)" << endl; } //overload,两个f1函数在Base类的内部被 重载overload 16 };
- 派生类相关的代码
1 class DeriveA : public Base 2 { 3 public: 4 void display() override 5 { 6 Base::display();//在派生类中调用基类的虚函数 7 cout << "DeriveA display()" << endl; 8 } //override,基类中display为虚函数,故此处为重写 9 10 void f1(int a, int b) { cout << "DeriveA f1(int,int)" << endl; } //redefining,f1函数在Base类中不为虚函数,故此处为 重定义redefining 11 void say() { cout << "DeriveA say()" << endl; } //redefining,同上 12 }; 13 14 15 class DeriveB : public Base 16 { 17 public: 18 void f1(int a) { cout << "DeriveB f1(int)" << endl; } //redefining,重定义 19 };
在派生类DeriveA中,重写了基类Base中的display函数,除此之外,重定义了基类Base中的f1函数以及say函数。
在派生类DeriveB中,重定义了基类Base中的f1函数。
则如下代码的输出:
1 DeriveA Da; 2 DeriveB Db; 3 Base* B = dynamic_cast<Base*>(&Da); 4 5 /*---*/ 6 Da.exec(); 7 Da.say(); 8 Da.displayinBase(); 9 /*---*/ 10 Db.f1(1); 11 /*---*/ 12 B->exec(); 13 B->display();
申请一个派生类Da以及一个派生类Db。
将派生类的对象Da强制转化为指向基类Base的对象B。
- Da.exec()
派生类中的exec函数使用的是基类Base中的exec函数,即exec函数调用的是Base中display函数和say函数,其中派生类DeriveA中重写了display函数,所以输出结果为:
DeriveA display()
Base say()
- Da.say() 派生类deriveA中重定义、覆盖了Base中的say函数,直接输出即可:
DeriveA say()
- Da.displayinBase() 要想在派生类中直接调用重载之前的基类函数,需要使用Base::display()
Base display()
- Db.f1(1)
DeriveB中重定义、覆盖了Base中的f1函数,直接输出即可:
DeriveB f1(int)
- B->exec() B指向的Base类是由派生类DeriveA强转过来的。调用exec函数即调用display函数和say函数,其中display被DeriveA类重写,say是自己的,即输出:
DeriveA display()
Base say()
- B->display() 基类中的display函数被DeriveA类重写,即输出:
DeriveA display()
1 将子类DeriveA强转为基类Base之后( dynamic_cast<Base*>(&DeriverA) ),调用重写函数是调用的deriveA中的,调用重定义函数是调用Base中的。
虚函数
虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。
1 classA 2 { 3 public: 4 virtual void foo() { cout << "A::foo() iscalled" << endl;} 5 }; 6 7 classB: public A 8 { 9 public: 10 virtual void foo() { cout << "B::foo() iscalled" << endl;} 11 };
那么,在使用的时候,我们可以:
A* a = new B();
a->foo(); // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!
这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。
它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。
由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
虚函数只能借助于指针或者引用来达到多态的效果。