类成员函数的重载、覆盖和隐藏的区别
1、成员函数的重载
函数名相同,但是参数的个数或参数的类型不同,编译后重载的函数都具有不同的地址,重载可以针对运算符,而覆盖不行.
成员函数被重载的特征:
a. 相同的范围,作用域(同一个类中)
b. 函数名相同,参数不同
c. virtual关键字可有可无
class Animal
{
public:
void eat(char a);
void eat(char a,int b);
void eat(int a,char b);
};
在同一个类Animal中,有3个同名的成员函数,但是参数类型互不相同,它们互为重载函数。
注意:重载函数和返回值的类型无关。
2、覆盖
也叫重写,只对类的构造函数或成员函数适用,是子类继承父类时才使用的非常有用的功能,真正和多态相关,它们的地址在编译时无法确定,在运行时动态分配。
派生类函数覆盖基类函数,特征是:
a. 不同的范围(分别位于派生类与基类)
b. 函数名相同,参数相同(类型相同,顺序相同),返回值类型相同
所有都必须和基类中被重写的函数一致,只有函数体不同。
c. 基类函数必须有virtual关键字
class Animal
{
public:
virtual void eat(char a)
{
cout<<"Animal::eat() "<<a<<endl;
}
};
//Cat公有继承Animal,否则基类的指针不能指向派生类
class Cat:public Animal
{
public:
//重写了基类的eat()函数,函数名参数都相同,基类有Virtual,子类和派生类之间。
void eat(char b)
{
cout<<"Cat::eat() "<<b<<endl;
}
};
int main()
{
Animal dongwu;
Cat mao;
Animal *p = &dongwu;
p->eat('g');
dongwu.eat('g');
p = &mao;
p->eat('r');
mao.eat('r');
system("pause");
return 0;
}
输出显示:
3、隐藏
指派生类的函数屏蔽了与其同名的基类函数
a. 如果派生类的函数与基类的函数同名,但是参数不同,此时,无论有无virtual关键字,基类的函数将被隐藏
b. 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字, 此时,基类的函数被隐藏。
1)子类和父类之间的同名函数,参数不同
class Animal
{
public:
void eat(char a)
{
cout<<"Animal::eat() "<<a<<endl;
}
};
class Cat:public Animal
{
public:
//隐藏,函数名相同,参数不同,有无virtual无所谓
void eat(char c,int i)
{
cout<<"Cat::eat() "<<c<<" and "<<i<<endl;
}
};
int main()
{
Cat mao;
mao.eat('r',5);
//mao.eat('g');//出错,因为基类的函数被隐藏
system("pause");
return 0;
}
2)参数相同,无virtual关键字
class Animal
{
public:
void eat(char a)
{
cout<<"Animal::eat() "<<a<<endl;
}
};
class Cat:public Animal
{
public:
//隐藏,函数名相同,参数不同,无virtual,和返回值的类型无关
bool eat(char c)
{
cout<<"Cat::eat() "<<c<<endl;
return true;
}
};
int main()
{
Cat mao;
mao.eat('r');//调用Cat类的eat函数
system("pause");
return 0;
}
举例:
1)重写和隐藏
#include <iostream>
using namespace std;
class Base
{
public:
virtual void eat()
{
cout<<"Base"<<endl;
}
};
class Child1:public Base
{
public:
void eat()
{
cout<<"Child1"<<endl;
}
};
class Child2:public Base
{
public:
void eat(int n)
{
cout<<"Child2"<<endl;
}
};
int main()
{
Base ba;
Child1 c1;
Child2 c2;
/*
重写的特点
1)子类和父类之间的同名虚函数,基类必须有virtual。
2) 函数的名字相同,参数相同,返回值相同,所有都相同,除了(所在的范围和函数体)
*/
Base *p;//定义一个基类的指针
//** 指针p指向Base的对象
p = &ba;
p->eat();//调用Base的eat()函数
//** 指针p指向Child1的对象
p = &c1;
p->eat();//Child1继承了Base并重写了eat()虚函数,所以会动态调用重写后的eat()
//** 指针p指向Child2的对象
p = &c2;
p->eat();//Child2也继承了Base,并重新定义了一个eat(int)带一个参数的函数,这不叫重写,是隐藏,所以还会调用Base的eat();
//c2.eat();
c2.eat(2);//会调用隐藏的那个函数。
system("pause");
return 0;
}
输出结果:
2)重载和重写
#include<stdio.h>
class A
{
public:
void FuncA()
{
printf("FuncA called\n");
}
virtual void FuncB()
{
printf("FuncB called\n");
}
};
class B:public A
{
public:
void FuncA()
{
A::FuncA();
printf("FuncAB called\n");
}
virtual void FuncB()
{
printf("FuncBB called\n");
}
};
void main()
{
B b;
A *pa;
pa=&b;
A *pa2=new A;
b.FuncA();
printf("*\n");
b.FuncB();
printf("*\n");
pa->FuncA();
printf("*\n");
pa->FuncB();
printf("*\n");
pa2->FuncA();
printf("*\n");
pa2->FuncB();
delete pa2;
}
输出结果: