主要有三种关系:重载(overload),覆盖(重写)(override),隐藏(重定义)(hide,oversee)
一、重载(overload)-水平关系
指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
(1)相同的范围(在同一个类中) ;
(2)函数名字相同;
(3)参数列表不同;
(4)virtual 关键字可有可无。
(5)返回值可以不同;
二、重写(也称为覆盖 override)-垂直关系
是指派生类重新定义基类的同名虚函数,特征是:
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)基类函数必须有 virtual 关键字,不能有 static 。
(3)与基类虚函数参数类型相同;
(4)与基类虚函数参数个数相同;
(5)与基类虚函数返回值相同(或是协变),否则报错;<—-协变这个概念我也是第一次才知道…
(6)重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的
三、重定义(也成隐藏)
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)函数名字相同;
(3)返回值可以不同;
(4)参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载以及覆盖混淆) 。
(5)参数相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) 。
下面通过实际的例子来看看具体的调用情况,同时可以用gdb看看运行的内存地址分布
#include <iostream>
using namespace std;
class A
{
public:
int oo;
char p;
int pp;
long long q;
char a;
public:
virtual void g()
{
cout << "A::virtual void g() called" << endl;
}
virtual void f()
{
cout << "A::virtual void f() called" << endl;
}
/* void f()
{
cout<<"A::void f() called"<<endl;
}
*/
void lambda()
{
cout << " A::void lambda() called:" << endl;
}
//同一个类,通过参数列表不同,重载了lambda()
//如果定义virtual void lambda(int a,int b),由于子类有不同的返回值,将产生重写overriding失败
void lambda(int a,int b)
{
cout<<"A::void lambda(int a,int b) called:"<<a+b<<endl;
}
//同一个类,通过返回值不同,重载了lambda()
int lambda(long long a,long long b)
{
cout<<"A::int lambda(long long a,long long b) called:"<<endl;
return a>b;
}
};
class B : public A
{
public:
//char c;
long long d;
public:
//不同类,注意基类g()有virtual关键字,且参数一致,故称之为重写或者覆盖
void g()
{
cout << "B::void g()" << endl;
}
//不同类,基类有virtual关键字,同理,属于重写或覆盖
//同一类,同名函数,参数列表一致,只是加了virtual关键字,怎么算?算重载错误,overload不允许,也是说同一个类不能拿有没有virtual关键字进行重载
/*
virtual void g()
{
cout << "B::virtual void g()" << endl;
}*/
//不同类中,基类g函数为虚函数,函数g改变了参数列表,不是重写或者覆盖,那就是属于重定义
//同一类中,利用参数列表不同重载了g
virtual void g(int a,int b){
cout << "B::virtual g(int a,int b)" << endl;
}
//基类f为虚函数,不同类中参数列表一致,重写(覆盖)了f
void f()
{
cout<<"B::void f()"<<endl;
}
//不同类中,基类lambda为普通函数,参数列表相同,隐藏或者说重定义了基类的lambda
void lambda()
{
cout << "B::void lambda()"<<endl;
}
//不同类中,基类lambda为普通函数,参数列表一致,返回值不同,重定义了lambda,属于重写?no,只有基类为virtual才算重写,不然归类为重定义
int lambda(int a,int b)
{
cout<<"B::int lambda(int a,int b)"<<endl;
return a+b;
}
};
typedef void( *Fun )( void );
int main(int argc, char* argv[])
{
B b;
Fun pFun;
cout<<sizeof(b)<<endl;
//如何找到一个类的虚函数列表,注意这里很好奇gcc为何会每个虚函数地址后面跟着一个0地址作为结尾符
cout<<"--------------find virtual tables test:-------------"<<endl;
for(int i = 0 ; i < 5; i=i+2)
{
pFun = (Fun)*((int*)*(int*)(&b)+i);
pFun();
}
//多态测试
cout<<"----------------A multi situation test:---------------"<<endl;
A *base = new B();
base->lambda(2,3);
long long x=9,y=10;
base->lambda(x,y);
base->g();
base->f();
//子类虚函数重载测试
B *b_base = new B();
cout<<"-----------------------B's f virtual func:--------------"<<endl;
b_base->lambda();
b_base->lambda(2,3);
b_base->f();
b_base->g();
b_base->g(3,3);
//父类函数测试
cout<<"-------------------------A's func test:------------------"<<endl;
A *a_base = new A();
a_base->f();
a_base->lambda(6,7);
return 0;
}
输出结果:
48
————–find virtual tables test:————-
B::void g()
B::void f()
B::virtual g(int a,int b)
—————-A multi situation test:—————
A::void lambda(int a,int b) called:5
A::int lambda(long long a,long long b) called:
B::void g()
B::void f()
———————–B’s f virtual func:————–
B::void lambda()
B::int lambda(int a,int b)
B::void f()
B::void g()
B::virtual g(int a,int b)
————————-A’s func test:——————
A::virtual void f() called
A::void lambda(int a,int b) called:13
数据成员地址为:
(gdb) p &b
8 = (B *) 0x7fffffffe530
(gdb) p &b.oo9 = (int *) 0x7fffffffe538
(gdb) p &b.p
10 = 0x7fffffffe53c “”
(gdb) p &b.pp11 = (int *) 0x7fffffffe540
(gdb) p &b.a
12 = (long long *) 0x7fffffffe548
(gdb) p &b.d13 = 0x7fffffffe550 “”
(gdb) p &b.q
$14 = (long long *) 0x7fffffffe558